jorgex-stack 1.9.38 → 1.9.40
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/README.md +5 -5
- package/dist/cli.js +779 -480
- package/package.json +1 -1
- package/stack/system-prompt/AGENTS.md +4 -13
- package/stack/system-prompt/context7.md +6 -0
package/dist/cli.js
CHANGED
|
@@ -15,12 +15,12 @@ import {
|
|
|
15
15
|
|
|
16
16
|
// src/cli.ts
|
|
17
17
|
import * as p6 from "@clack/prompts";
|
|
18
|
-
import
|
|
18
|
+
import fs27 from "fs";
|
|
19
19
|
import { pathToFileURL as pathToFileURL2 } from "url";
|
|
20
20
|
|
|
21
21
|
// src/install.ts
|
|
22
|
-
import
|
|
23
|
-
import
|
|
22
|
+
import fs17 from "fs";
|
|
23
|
+
import path22 from "path";
|
|
24
24
|
import * as p from "@clack/prompts";
|
|
25
25
|
|
|
26
26
|
// src/lib/writing-style.ts
|
|
@@ -498,15 +498,81 @@ function applyWritingStyle(plan, dryRun = false) {
|
|
|
498
498
|
writeText(plan.sourcePath, plan.installedContent, mode);
|
|
499
499
|
}
|
|
500
500
|
|
|
501
|
+
// src/lib/system-prompt-sections.ts
|
|
502
|
+
import fs4 from "fs";
|
|
503
|
+
import path4 from "path";
|
|
504
|
+
var SYSTEM_PROMPT_SECTIONS = [
|
|
505
|
+
"system-prompt",
|
|
506
|
+
"engram-protocol",
|
|
507
|
+
"context7",
|
|
508
|
+
"playwright",
|
|
509
|
+
"chrome-devtools",
|
|
510
|
+
"browser",
|
|
511
|
+
"writing-style"
|
|
512
|
+
];
|
|
513
|
+
function assertSystemPromptMarkers(content, target) {
|
|
514
|
+
if (content === null) return;
|
|
515
|
+
const ranges = [];
|
|
516
|
+
for (const section of SYSTEM_PROMPT_SECTIONS) {
|
|
517
|
+
const markers2 = [...content.matchAll(new RegExp(`<!--\\s*\\/?jorgex:${section}(?=[\\s>]|-->|$)`, "g"))];
|
|
518
|
+
if (markers2.length === 0) continue;
|
|
519
|
+
if (markers2.length !== 2 || !hasHealthyManagedMarkdownMarkers(content, section)) {
|
|
520
|
+
throw new Error(`Marcadores jorgex:${section} ambiguos en ${target}. Repara el bloque antes de reintentar; su contenido se conserva.`);
|
|
521
|
+
}
|
|
522
|
+
ranges.push({
|
|
523
|
+
section,
|
|
524
|
+
start: content.indexOf(`<!-- jorgex:${section} -->`),
|
|
525
|
+
end: content.indexOf(`<!-- /jorgex:${section} -->`) + `<!-- /jorgex:${section} -->`.length
|
|
526
|
+
});
|
|
527
|
+
}
|
|
528
|
+
ranges.sort((a, b) => a.start - b.start);
|
|
529
|
+
for (let index = 1; index < ranges.length; index++) {
|
|
530
|
+
if (ranges[index].start < ranges[index - 1].end) {
|
|
531
|
+
throw new Error(`Marcadores jorgex:${ranges[index].section} anidados en ${target}. Separa los bloques antes de reintentar; su contenido se conserva.`);
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
function readSystemPromptFile(target, rootDir) {
|
|
536
|
+
if (rootDir !== void 0) {
|
|
537
|
+
const probe = fs4.existsSync(target) ? target : path4.dirname(target);
|
|
538
|
+
if (fs4.existsSync(probe) && fs4.existsSync(rootDir)) {
|
|
539
|
+
const resolved = fs4.realpathSync(probe);
|
|
540
|
+
const root = fs4.realpathSync(rootDir);
|
|
541
|
+
if (resolved !== root && !isContainedIn(resolved, root)) {
|
|
542
|
+
throw new Error(`El prompt ${target} sale del destino aislado.`);
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
let bytes;
|
|
547
|
+
try {
|
|
548
|
+
bytes = fs4.readFileSync(target);
|
|
549
|
+
} catch (error) {
|
|
550
|
+
if (error.code === "ENOENT") return null;
|
|
551
|
+
throw new Error(`No se puede leer el prompt ${target}. Revisa los permisos y el tipo de archivo.`, { cause: error });
|
|
552
|
+
}
|
|
553
|
+
try {
|
|
554
|
+
return new TextDecoder("utf-8", { fatal: true, ignoreBOM: true }).decode(bytes);
|
|
555
|
+
} catch (error) {
|
|
556
|
+
throw new Error(`El prompt ${target} debe usar UTF-8 v\xE1lido.`, { cause: error });
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
function assertSystemPromptFile(target, rootDir) {
|
|
560
|
+
assertSystemPromptMarkers(readSystemPromptFile(target, rootDir), target);
|
|
561
|
+
}
|
|
562
|
+
function removeSystemPromptSections(content) {
|
|
563
|
+
assertSystemPromptMarkers(content, "system prompt");
|
|
564
|
+
return SYSTEM_PROMPT_SECTIONS.reduce((current, section) => removeMarkdownSection(current, section), content);
|
|
565
|
+
}
|
|
566
|
+
|
|
501
567
|
// src/adapters/opencode.ts
|
|
502
|
-
import
|
|
503
|
-
import
|
|
568
|
+
import path9 from "path";
|
|
569
|
+
import fs6 from "fs";
|
|
504
570
|
import { isDeepStrictEqual } from "util";
|
|
505
571
|
import { pathToFileURL } from "url";
|
|
506
572
|
|
|
507
573
|
// src/lib/canonical.ts
|
|
508
|
-
import
|
|
509
|
-
import
|
|
574
|
+
import path5 from "path";
|
|
575
|
+
import fs5 from "fs";
|
|
510
576
|
function parseCanonicalAgent(source, file) {
|
|
511
577
|
const match = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?/.exec(source);
|
|
512
578
|
if (!match) throw new Error(`Agente sin frontmatter: ${file}`);
|
|
@@ -532,9 +598,9 @@ function parseCanonicalAgent(source, file) {
|
|
|
532
598
|
};
|
|
533
599
|
}
|
|
534
600
|
function loadCanonicalAgents(agentsDir) {
|
|
535
|
-
return
|
|
536
|
-
const file =
|
|
537
|
-
const source =
|
|
601
|
+
return fs5.readdirSync(agentsDir).filter((f) => f.endsWith(".md") && f.toLowerCase() !== "readme.md").map((f) => {
|
|
602
|
+
const file = path5.join(agentsDir, f);
|
|
603
|
+
const source = fs5.readFileSync(file, "utf8").replace(/\r\n/g, "\n");
|
|
538
604
|
return parseCanonicalAgent(source, file);
|
|
539
605
|
});
|
|
540
606
|
}
|
|
@@ -543,21 +609,21 @@ function isCanonicalMcpServerEnabled(name, server, enabledServers) {
|
|
|
543
609
|
return !server.optional || server.defaultEnabled === true || enabledServers?.has(name) === true;
|
|
544
610
|
}
|
|
545
611
|
function loadCanonicalMcp(stackDir) {
|
|
546
|
-
return JSON.parse(
|
|
612
|
+
return JSON.parse(fs5.readFileSync(path5.join(stackDir, "mcp", "servers.json"), "utf8"));
|
|
547
613
|
}
|
|
548
614
|
function loadCanonicalHooks(stackDir) {
|
|
549
|
-
return JSON.parse(
|
|
615
|
+
return JSON.parse(fs5.readFileSync(path5.join(stackDir, "hooks", "hooks.json"), "utf8"));
|
|
550
616
|
}
|
|
551
617
|
function loadCanonicalDefaults(stackDir) {
|
|
552
|
-
const file =
|
|
553
|
-
if (!
|
|
554
|
-
const parsed = JSON.parse(
|
|
618
|
+
const file = path5.join(stackDir, "config", "defaults.json");
|
|
619
|
+
if (!fs5.existsSync(file)) return {};
|
|
620
|
+
const parsed = JSON.parse(fs5.readFileSync(file, "utf8"));
|
|
555
621
|
delete parsed["$comment"];
|
|
556
622
|
return parsed;
|
|
557
623
|
}
|
|
558
624
|
|
|
559
625
|
// src/lib/model-map.ts
|
|
560
|
-
import
|
|
626
|
+
import path6 from "path";
|
|
561
627
|
import { existsSync } from "fs";
|
|
562
628
|
function resolveAgentModel(models, agentName, tier) {
|
|
563
629
|
const base = models[tier];
|
|
@@ -584,7 +650,7 @@ var DEFAULT_MODEL_MAP = {
|
|
|
584
650
|
}
|
|
585
651
|
};
|
|
586
652
|
function modelMapFile() {
|
|
587
|
-
return
|
|
653
|
+
return path6.join(dataDir(), "model-map.json");
|
|
588
654
|
}
|
|
589
655
|
function loadModelMap() {
|
|
590
656
|
const file = modelMapFile();
|
|
@@ -612,15 +678,15 @@ function ensureModelMapFile() {
|
|
|
612
678
|
}
|
|
613
679
|
|
|
614
680
|
// src/lib/detect.ts
|
|
615
|
-
import
|
|
681
|
+
import path7 from "path";
|
|
616
682
|
import { existsSync as existsSync2, statSync } from "fs";
|
|
617
683
|
import { execFileSync } from "child_process";
|
|
618
|
-
function lookPath(cmd) {
|
|
684
|
+
function lookPath(cmd, env = process.env) {
|
|
619
685
|
const exts = process.platform === "win32" ? [".exe", ".cmd", ".bat", ".ps1", ""] : [""];
|
|
620
|
-
for (const dir of (
|
|
686
|
+
for (const dir of (env.PATH ?? "").split(path7.delimiter)) {
|
|
621
687
|
if (!dir) continue;
|
|
622
688
|
for (const ext of exts) {
|
|
623
|
-
const candidate =
|
|
689
|
+
const candidate = path7.join(dir, cmd + ext);
|
|
624
690
|
if (statSync(candidate, { throwIfNoEntry: false })?.isFile()) return candidate;
|
|
625
691
|
}
|
|
626
692
|
}
|
|
@@ -650,7 +716,7 @@ function runDetectedBin(bin, args, timeoutMs, envOverrides) {
|
|
|
650
716
|
}
|
|
651
717
|
}
|
|
652
718
|
function detectOpenCode() {
|
|
653
|
-
const configDir = process.env.OPENCODE_CONFIG_DIR ??
|
|
719
|
+
const configDir = process.env.OPENCODE_CONFIG_DIR ?? path7.join(HOME, ".config", "opencode");
|
|
654
720
|
const binPath = lookPath("opencode");
|
|
655
721
|
return {
|
|
656
722
|
id: "opencode",
|
|
@@ -661,7 +727,7 @@ function detectOpenCode() {
|
|
|
661
727
|
};
|
|
662
728
|
}
|
|
663
729
|
function detectClaudeCode() {
|
|
664
|
-
const configDir =
|
|
730
|
+
const configDir = path7.join(HOME, ".claude");
|
|
665
731
|
const binPath = lookPath("claude");
|
|
666
732
|
return {
|
|
667
733
|
id: "claude-code",
|
|
@@ -672,7 +738,7 @@ function detectClaudeCode() {
|
|
|
672
738
|
};
|
|
673
739
|
}
|
|
674
740
|
function detectCodex() {
|
|
675
|
-
const configDir = process.env.CODEX_HOME ??
|
|
741
|
+
const configDir = process.env.CODEX_HOME ?? path7.join(HOME, ".codex");
|
|
676
742
|
const binPath = lookPath("codex");
|
|
677
743
|
return {
|
|
678
744
|
id: "codex",
|
|
@@ -688,19 +754,19 @@ function detectEngram() {
|
|
|
688
754
|
const onPath = lookPath("engram");
|
|
689
755
|
if (onPath) return onPath;
|
|
690
756
|
const candidates = [
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
757
|
+
path7.join(HOME, "go", "bin", "engram.exe"),
|
|
758
|
+
path7.join(HOME, "go", "bin", "engram"),
|
|
759
|
+
path7.join(HOME, ".local", "bin", "engram.exe"),
|
|
760
|
+
path7.join(HOME, ".local", "bin", "engram")
|
|
695
761
|
];
|
|
696
762
|
for (const c of candidates) if (existsSync2(c)) return c;
|
|
697
763
|
return null;
|
|
698
764
|
}
|
|
699
765
|
|
|
700
766
|
// src/lib/hooks-format.ts
|
|
701
|
-
import
|
|
767
|
+
import path8 from "path";
|
|
702
768
|
function hookScriptNames(canonical) {
|
|
703
|
-
return Object.values(canonical.hooks).flat().flatMap((entry) => entry.hooks).map((h) => /\{\{SCRIPTS_DIR\}\}[/\\]([\w./-]+)/.exec(h.command)?.[1]).filter((s) => s !== void 0).map((s) =>
|
|
769
|
+
return Object.values(canonical.hooks).flat().flatMap((entry) => entry.hooks).map((h) => /\{\{SCRIPTS_DIR\}\}[/\\]([\w./-]+)/.exec(h.command)?.[1]).filter((s) => s !== void 0).map((s) => path8.basename(s));
|
|
704
770
|
}
|
|
705
771
|
function plainHookCommands(canonical) {
|
|
706
772
|
return new Set(
|
|
@@ -747,7 +813,7 @@ function upsertNativeHooks(existing, canonical, scriptsDir, mapMatcher = (m) =>
|
|
|
747
813
|
...h.timeout !== void 0 ? { timeout: h.timeout } : {}
|
|
748
814
|
}))
|
|
749
815
|
};
|
|
750
|
-
const scriptNames = entry.hooks.map((h) => /\{\{SCRIPTS_DIR\}\}[/\\]([\w./-]+)/.exec(h.command)?.[1]).filter((s) => s !== void 0).map((s) =>
|
|
816
|
+
const scriptNames = entry.hooks.map((h) => /\{\{SCRIPTS_DIR\}\}[/\\]([\w./-]+)/.exec(h.command)?.[1]).filter((s) => s !== void 0).map((s) => path8.basename(s));
|
|
751
817
|
const plainCommands = entry.hooks.map((h) => h.command).filter((c) => !c.includes("{{SCRIPTS_DIR}}"));
|
|
752
818
|
const index = list.findIndex((existingEntry) => {
|
|
753
819
|
const e = existingEntry;
|
|
@@ -891,7 +957,7 @@ function pruneEmpty(parent, key) {
|
|
|
891
957
|
if (value !== null && Object.keys(value).length === 0) delete parent[key];
|
|
892
958
|
}
|
|
893
959
|
function hasOpenCodeManualApproval(configDir) {
|
|
894
|
-
const content = readTextIfExists(
|
|
960
|
+
const content = readTextIfExists(path9.join(configDir, "opencode.json"));
|
|
895
961
|
if (content === null) return false;
|
|
896
962
|
try {
|
|
897
963
|
const root = JSON.parse(content);
|
|
@@ -907,7 +973,7 @@ var opencodeAdapter = {
|
|
|
907
973
|
name: "OpenCode",
|
|
908
974
|
detect: detectOpenCode,
|
|
909
975
|
reportCapabilities(configDir) {
|
|
910
|
-
const prompt = readTextIfExists(
|
|
976
|
+
const prompt = readTextIfExists(path9.join(configDir, "AGENTS.md"));
|
|
911
977
|
return createLocalCapabilityReport("opencode", [
|
|
912
978
|
...hasManagedMarkdownSection(prompt, "system-prompt") ? [{
|
|
913
979
|
id: "policy-guidance",
|
|
@@ -929,16 +995,16 @@ var opencodeAdapter = {
|
|
|
929
995
|
paths(configDir) {
|
|
930
996
|
const isRealConfigDir = samePath(
|
|
931
997
|
configDir,
|
|
932
|
-
process.env.OPENCODE_CONFIG_DIR ??
|
|
998
|
+
process.env.OPENCODE_CONFIG_DIR ?? path9.join(HOME, ".config", "opencode")
|
|
933
999
|
);
|
|
934
|
-
const agentsHome = isRealConfigDir ? HOME :
|
|
1000
|
+
const agentsHome = isRealConfigDir ? HOME : path9.dirname(configDir);
|
|
935
1001
|
return {
|
|
936
|
-
systemPromptFile:
|
|
937
|
-
agentsDir:
|
|
938
|
-
skillsDir:
|
|
939
|
-
commandsDir:
|
|
940
|
-
pluginsDir:
|
|
941
|
-
scriptsDir:
|
|
1002
|
+
systemPromptFile: path9.join(configDir, "AGENTS.md"),
|
|
1003
|
+
agentsDir: path9.join(configDir, "agents"),
|
|
1004
|
+
skillsDir: path9.join(agentsHome, ".agents", "skills"),
|
|
1005
|
+
commandsDir: path9.join(configDir, "commands"),
|
|
1006
|
+
pluginsDir: path9.join(configDir, "plugins"),
|
|
1007
|
+
scriptsDir: path9.join(configDir, "scripts"),
|
|
942
1008
|
outputStylesDir: null,
|
|
943
1009
|
profilesDir: null
|
|
944
1010
|
};
|
|
@@ -996,12 +1062,12 @@ ${agent.body}`,
|
|
|
996
1062
|
ctx.warnings.push(`opencode: hook sin {{SCRIPTS_DIR}} no traducible: ${hook.command}`);
|
|
997
1063
|
continue;
|
|
998
1064
|
}
|
|
999
|
-
const script = `scripts/${
|
|
1065
|
+
const script = `scripts/${path9.basename(match[1])}`;
|
|
1000
1066
|
(bashEntries[includes] ??= []).push(script);
|
|
1001
1067
|
}
|
|
1002
1068
|
}
|
|
1003
1069
|
}
|
|
1004
|
-
const hooksFile =
|
|
1070
|
+
const hooksFile = path9.join(ctx.configDir, "hooks.json");
|
|
1005
1071
|
const content = upsertJson(readTextIfExists(hooksFile), (root) => {
|
|
1006
1072
|
const afterValue = root["tool.execute.after"] ??= {};
|
|
1007
1073
|
if (afterValue === null || typeof afterValue !== "object" || Array.isArray(afterValue)) {
|
|
@@ -1036,16 +1102,16 @@ ${agent.body}`,
|
|
|
1036
1102
|
}
|
|
1037
1103
|
});
|
|
1038
1104
|
actions.push({ kind: "write", target: hooksFile, content });
|
|
1039
|
-
const scriptsSource =
|
|
1040
|
-
if (
|
|
1041
|
-
for (const f of
|
|
1042
|
-
actions.push({ kind: "copy", source:
|
|
1105
|
+
const scriptsSource = path9.join(ctx.stackDir, "scripts");
|
|
1106
|
+
if (fs6.existsSync(scriptsSource)) {
|
|
1107
|
+
for (const f of fs6.readdirSync(scriptsSource)) {
|
|
1108
|
+
actions.push({ kind: "copy", source: path9.join(scriptsSource, f), target: path9.join(scriptsDir, f) });
|
|
1043
1109
|
}
|
|
1044
1110
|
}
|
|
1045
1111
|
return actions;
|
|
1046
1112
|
},
|
|
1047
1113
|
planMainConfig(canonical, ctx) {
|
|
1048
|
-
const file =
|
|
1114
|
+
const file = path9.join(ctx.configDir, "opencode.json");
|
|
1049
1115
|
const { pluginsDir } = this.paths(ctx.configDir);
|
|
1050
1116
|
const original = readTextIfExists(file);
|
|
1051
1117
|
const contentSource = original === null || original.trim() === "" ? null : original;
|
|
@@ -1152,13 +1218,10 @@ ${agent.body}`,
|
|
|
1152
1218
|
const { systemPromptFile, pluginsDir } = this.paths(ctx.configDir);
|
|
1153
1219
|
const prompt = readTextIfExists(systemPromptFile);
|
|
1154
1220
|
if (prompt !== null) {
|
|
1155
|
-
|
|
1156
|
-
content = removeMarkdownSection(content, "engram-protocol");
|
|
1157
|
-
content = removeMarkdownSection(content, "browser");
|
|
1158
|
-
content = removeMarkdownSection(content, "writing-style");
|
|
1221
|
+
const content = removeSystemPromptSections(prompt);
|
|
1159
1222
|
actions.push({ kind: "write", target: systemPromptFile, content });
|
|
1160
1223
|
}
|
|
1161
|
-
const configFile =
|
|
1224
|
+
const configFile = path9.join(ctx.configDir, "opencode.json");
|
|
1162
1225
|
const config = readTextIfExists(configFile);
|
|
1163
1226
|
if (config !== null) {
|
|
1164
1227
|
const mcpOwnership = [];
|
|
@@ -1226,7 +1289,7 @@ ${agent.body}`,
|
|
|
1226
1289
|
...primaryModelOwnership.length > 0 ? { primaryModelOwnership } : {}
|
|
1227
1290
|
});
|
|
1228
1291
|
}
|
|
1229
|
-
const hooksFile =
|
|
1292
|
+
const hooksFile = path9.join(ctx.configDir, "hooks.json");
|
|
1230
1293
|
const hooksJson = readTextIfExists(hooksFile);
|
|
1231
1294
|
if (hooksJson !== null) {
|
|
1232
1295
|
const ourScripts = hookScriptNames(hooks);
|
|
@@ -1252,8 +1315,8 @@ ${agent.body}`,
|
|
|
1252
1315
|
};
|
|
1253
1316
|
|
|
1254
1317
|
// src/adapters/claude-code.ts
|
|
1255
|
-
import
|
|
1256
|
-
import
|
|
1318
|
+
import path10 from "path";
|
|
1319
|
+
import fs7 from "fs";
|
|
1257
1320
|
import { isDeepStrictEqual as isDeepStrictEqual2 } from "util";
|
|
1258
1321
|
function yamlString2(value) {
|
|
1259
1322
|
return JSON.stringify(value);
|
|
@@ -1269,7 +1332,7 @@ function isRecord(value) {
|
|
|
1269
1332
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
1270
1333
|
}
|
|
1271
1334
|
function hasClaudeManualApproval(configDir) {
|
|
1272
|
-
const content = readTextIfExists(
|
|
1335
|
+
const content = readTextIfExists(path10.join(configDir, "settings.json"));
|
|
1273
1336
|
if (content === null) return false;
|
|
1274
1337
|
try {
|
|
1275
1338
|
const root = JSON.parse(content);
|
|
@@ -1289,8 +1352,8 @@ function toolsFor(agent) {
|
|
|
1289
1352
|
return tools.join(", ");
|
|
1290
1353
|
}
|
|
1291
1354
|
function hasEngramPlugin(configDir) {
|
|
1292
|
-
if (
|
|
1293
|
-
const registry = readTextIfExists(
|
|
1355
|
+
if (fs7.existsSync(path10.join(configDir, "plugins", "marketplaces", "engram"))) return true;
|
|
1356
|
+
const registry = readTextIfExists(path10.join(configDir, "plugins", "installed_plugins.json"));
|
|
1294
1357
|
if (registry === null) return false;
|
|
1295
1358
|
try {
|
|
1296
1359
|
const parsed = JSON.parse(registry);
|
|
@@ -1313,7 +1376,7 @@ var claudeCodeAdapter = {
|
|
|
1313
1376
|
name: "Claude Code",
|
|
1314
1377
|
detect: detectClaudeCode,
|
|
1315
1378
|
reportCapabilities(configDir) {
|
|
1316
|
-
const prompt = readTextIfExists(
|
|
1379
|
+
const prompt = readTextIfExists(path10.join(configDir, "CLAUDE.md"));
|
|
1317
1380
|
return createLocalCapabilityReport("claude-code", [
|
|
1318
1381
|
...hasManagedMarkdownSection(prompt, "system-prompt") ? [{
|
|
1319
1382
|
id: "policy-guidance",
|
|
@@ -1334,13 +1397,13 @@ var claudeCodeAdapter = {
|
|
|
1334
1397
|
},
|
|
1335
1398
|
paths(configDir) {
|
|
1336
1399
|
return {
|
|
1337
|
-
systemPromptFile:
|
|
1338
|
-
agentsDir:
|
|
1339
|
-
skillsDir:
|
|
1340
|
-
commandsDir:
|
|
1400
|
+
systemPromptFile: path10.join(configDir, "CLAUDE.md"),
|
|
1401
|
+
agentsDir: path10.join(configDir, "agents"),
|
|
1402
|
+
skillsDir: path10.join(configDir, "skills"),
|
|
1403
|
+
commandsDir: path10.join(configDir, "commands"),
|
|
1341
1404
|
pluginsDir: null,
|
|
1342
|
-
scriptsDir:
|
|
1343
|
-
outputStylesDir:
|
|
1405
|
+
scriptsDir: path10.join(configDir, "scripts"),
|
|
1406
|
+
outputStylesDir: path10.join(configDir, "output-styles"),
|
|
1344
1407
|
profilesDir: null
|
|
1345
1408
|
};
|
|
1346
1409
|
},
|
|
@@ -1386,9 +1449,9 @@ ${agent.body}`,
|
|
|
1386
1449
|
planHooks(canonical, ctx) {
|
|
1387
1450
|
const actions = [];
|
|
1388
1451
|
const { scriptsDir } = this.paths(ctx.configDir);
|
|
1389
|
-
const original = readTextIfExists(
|
|
1452
|
+
const original = readTextIfExists(path10.join(ctx.configDir, "settings.json"));
|
|
1390
1453
|
const contentSource = original === null || original.trim() === "" ? null : original;
|
|
1391
|
-
const settingsFile =
|
|
1454
|
+
const settingsFile = path10.join(ctx.configDir, "settings.json");
|
|
1392
1455
|
let content = upsertNativeHooks(contentSource, canonical, scriptsDir);
|
|
1393
1456
|
const defaults = loadCanonicalDefaults(ctx.stackDir)["claude-code"];
|
|
1394
1457
|
if (contentSource === null && defaults?.["permissions"] !== void 0) {
|
|
@@ -1402,16 +1465,16 @@ ${agent.body}`,
|
|
|
1402
1465
|
});
|
|
1403
1466
|
}
|
|
1404
1467
|
actions.push({ kind: "write", target: settingsFile, content });
|
|
1405
|
-
const scriptsSource =
|
|
1406
|
-
if (
|
|
1407
|
-
for (const f of
|
|
1408
|
-
actions.push({ kind: "copy", source:
|
|
1468
|
+
const scriptsSource = path10.join(ctx.stackDir, "scripts");
|
|
1469
|
+
if (fs7.existsSync(scriptsSource)) {
|
|
1470
|
+
for (const f of fs7.readdirSync(scriptsSource)) {
|
|
1471
|
+
actions.push({ kind: "copy", source: path10.join(scriptsSource, f), target: path10.join(scriptsDir, f) });
|
|
1409
1472
|
}
|
|
1410
1473
|
}
|
|
1411
1474
|
return actions;
|
|
1412
1475
|
},
|
|
1413
1476
|
planMainConfig(canonical, ctx) {
|
|
1414
|
-
const file =
|
|
1477
|
+
const file = path10.join(path10.dirname(ctx.configDir), `${path10.basename(ctx.configDir)}.json`);
|
|
1415
1478
|
const mcpOwnership = [];
|
|
1416
1479
|
const content = upsertJson(readTextIfExists(file), (root) => {
|
|
1417
1480
|
const servers = root["mcpServers"] ??= {};
|
|
@@ -1471,13 +1534,10 @@ ${agent.body}`,
|
|
|
1471
1534
|
const { systemPromptFile } = this.paths(ctx.configDir);
|
|
1472
1535
|
const prompt = readTextIfExists(systemPromptFile);
|
|
1473
1536
|
if (prompt !== null) {
|
|
1474
|
-
|
|
1475
|
-
content = removeMarkdownSection(content, "engram-protocol");
|
|
1476
|
-
content = removeMarkdownSection(content, "browser");
|
|
1477
|
-
content = removeMarkdownSection(content, "writing-style");
|
|
1537
|
+
const content = removeSystemPromptSections(prompt);
|
|
1478
1538
|
actions.push({ kind: "write", target: systemPromptFile, content });
|
|
1479
1539
|
}
|
|
1480
|
-
const settingsFile =
|
|
1540
|
+
const settingsFile = path10.join(ctx.configDir, "settings.json");
|
|
1481
1541
|
const settings = readTextIfExists(settingsFile);
|
|
1482
1542
|
if (settings !== null) {
|
|
1483
1543
|
const content = removeNativeHooks(settings, hooks);
|
|
@@ -1485,7 +1545,7 @@ ${agent.body}`,
|
|
|
1485
1545
|
actions.push({ kind: "write", target: settingsFile, content: content.trim() === "{}" ? "" : content });
|
|
1486
1546
|
}
|
|
1487
1547
|
}
|
|
1488
|
-
const mainFile =
|
|
1548
|
+
const mainFile = path10.join(path10.dirname(ctx.configDir), `${path10.basename(ctx.configDir)}.json`);
|
|
1489
1549
|
const main2 = readTextIfExists(mainFile);
|
|
1490
1550
|
if (main2 !== null) {
|
|
1491
1551
|
const mcpOwnership = [];
|
|
@@ -1511,8 +1571,8 @@ ${agent.body}`,
|
|
|
1511
1571
|
};
|
|
1512
1572
|
|
|
1513
1573
|
// src/adapters/codex.ts
|
|
1514
|
-
import
|
|
1515
|
-
import
|
|
1574
|
+
import path11 from "path";
|
|
1575
|
+
import fs8 from "fs";
|
|
1516
1576
|
function tomlString(value) {
|
|
1517
1577
|
return JSON.stringify(value);
|
|
1518
1578
|
}
|
|
@@ -1535,15 +1595,15 @@ function isManagedOptionalStdioServer3(server, section) {
|
|
|
1535
1595
|
return server.optional === true && server.transport === "stdio" && section?.trim() === stdioMcpSection(server);
|
|
1536
1596
|
}
|
|
1537
1597
|
function hasActiveEngramPlugin(configDir) {
|
|
1538
|
-
const config = readTextIfExists(
|
|
1598
|
+
const config = readTextIfExists(path11.join(configDir, "config.toml"));
|
|
1539
1599
|
if (config === null) return false;
|
|
1540
1600
|
const match = /\[plugins\."engram@[^"]*"\]([^[]*)/.exec(config);
|
|
1541
1601
|
return match !== null && !/enabled\s*=\s*false/.test(match[1]);
|
|
1542
1602
|
}
|
|
1543
1603
|
function hasEngramProtocol(configDir) {
|
|
1544
1604
|
if (hasActiveEngramPlugin(configDir)) return true;
|
|
1545
|
-
if (
|
|
1546
|
-
const config = readTextIfExists(
|
|
1605
|
+
if (fs8.existsSync(path11.join(configDir, "engram-instructions.md"))) return true;
|
|
1606
|
+
const config = readTextIfExists(path11.join(configDir, "config.toml"));
|
|
1547
1607
|
return config !== null && /engram-instructions\.md/.test(config);
|
|
1548
1608
|
}
|
|
1549
1609
|
var CODEX_JSON_STRING = String.raw`"(?:\\.|[^"\\\r\n])*"`;
|
|
@@ -1668,7 +1728,7 @@ function hasCanonicalCodexPermissionProfile(scan) {
|
|
|
1668
1728
|
return true;
|
|
1669
1729
|
}
|
|
1670
1730
|
function hasCodexManualApproval(configDir) {
|
|
1671
|
-
const config = readTextIfExists(
|
|
1731
|
+
const config = readTextIfExists(path11.join(configDir, "config.toml"));
|
|
1672
1732
|
if (config === null) return false;
|
|
1673
1733
|
const scan = scanCodexToml(config);
|
|
1674
1734
|
if (scan === null) return false;
|
|
@@ -1686,7 +1746,7 @@ var codexAdapter = {
|
|
|
1686
1746
|
name: "Codex CLI",
|
|
1687
1747
|
detect: detectCodex,
|
|
1688
1748
|
reportCapabilities(configDir) {
|
|
1689
|
-
const prompt = readTextIfExists(
|
|
1749
|
+
const prompt = readTextIfExists(path11.join(configDir, "AGENTS.md"));
|
|
1690
1750
|
return createLocalCapabilityReport("codex", [
|
|
1691
1751
|
...hasManagedMarkdownSection(prompt, "system-prompt") ? [{
|
|
1692
1752
|
id: "policy-guidance",
|
|
@@ -1711,18 +1771,18 @@ var codexAdapter = {
|
|
|
1711
1771
|
return !hasEngramProtocol(ctx.configDir);
|
|
1712
1772
|
},
|
|
1713
1773
|
paths(configDir) {
|
|
1714
|
-
const isRealConfigDir = samePath(configDir, process.env.CODEX_HOME ??
|
|
1715
|
-
const agentsHome = isRealConfigDir ? HOME :
|
|
1716
|
-
const skillsDir =
|
|
1774
|
+
const isRealConfigDir = samePath(configDir, process.env.CODEX_HOME ?? path11.join(HOME, ".codex"));
|
|
1775
|
+
const agentsHome = isRealConfigDir ? HOME : path11.dirname(configDir);
|
|
1776
|
+
const skillsDir = path11.join(agentsHome, ".agents", "skills");
|
|
1717
1777
|
return {
|
|
1718
|
-
systemPromptFile:
|
|
1719
|
-
agentsDir:
|
|
1778
|
+
systemPromptFile: path11.join(configDir, "AGENTS.md"),
|
|
1779
|
+
agentsDir: path11.join(configDir, "agents"),
|
|
1720
1780
|
skillsDir,
|
|
1721
1781
|
// Los custom prompts de Codex están deprecados: los commands se
|
|
1722
1782
|
// instalan como skills (renderCommand produce <nombre>/SKILL.md).
|
|
1723
1783
|
commandsDir: skillsDir,
|
|
1724
1784
|
pluginsDir: null,
|
|
1725
|
-
scriptsDir:
|
|
1785
|
+
scriptsDir: path11.join(configDir, "scripts"),
|
|
1726
1786
|
outputStylesDir: null,
|
|
1727
1787
|
profilesDir: configDir
|
|
1728
1788
|
};
|
|
@@ -1770,7 +1830,7 @@ ${body}`
|
|
|
1770
1830
|
planHooks(canonical, ctx) {
|
|
1771
1831
|
const actions = [];
|
|
1772
1832
|
const { scriptsDir } = this.paths(ctx.configDir);
|
|
1773
|
-
const hooksFile =
|
|
1833
|
+
const hooksFile = path11.join(ctx.configDir, "hooks.json");
|
|
1774
1834
|
const content = upsertNativeHooks(
|
|
1775
1835
|
readTextIfExists(hooksFile),
|
|
1776
1836
|
canonical,
|
|
@@ -1781,16 +1841,16 @@ ${body}`
|
|
|
1781
1841
|
ctx.warnings.push(
|
|
1782
1842
|
"Codex: los hooks no-managed requieren aprobaci\xF3n manual \u2014 ejecuta /hooks dentro de codex para activarlos."
|
|
1783
1843
|
);
|
|
1784
|
-
const scriptsSource =
|
|
1785
|
-
if (
|
|
1786
|
-
for (const f of
|
|
1787
|
-
actions.push({ kind: "copy", source:
|
|
1844
|
+
const scriptsSource = path11.join(ctx.stackDir, "scripts");
|
|
1845
|
+
if (fs8.existsSync(scriptsSource)) {
|
|
1846
|
+
for (const f of fs8.readdirSync(scriptsSource)) {
|
|
1847
|
+
actions.push({ kind: "copy", source: path11.join(scriptsSource, f), target: path11.join(scriptsDir, f) });
|
|
1788
1848
|
}
|
|
1789
1849
|
}
|
|
1790
1850
|
return actions;
|
|
1791
1851
|
},
|
|
1792
1852
|
planMainConfig(canonical, ctx) {
|
|
1793
|
-
const file =
|
|
1853
|
+
const file = path11.join(ctx.configDir, "config.toml");
|
|
1794
1854
|
const original = readTextIfExists(file);
|
|
1795
1855
|
const contentSource = original === null || original.trim() === "" ? null : original;
|
|
1796
1856
|
let content = contentSource;
|
|
@@ -1904,13 +1964,10 @@ args = [${(server.args ?? []).map(tomlString).join(", ")}]`);
|
|
|
1904
1964
|
const { systemPromptFile } = this.paths(ctx.configDir);
|
|
1905
1965
|
const prompt = readTextIfExists(systemPromptFile);
|
|
1906
1966
|
if (prompt !== null) {
|
|
1907
|
-
|
|
1908
|
-
content = removeMarkdownSection(content, "engram-protocol");
|
|
1909
|
-
content = removeMarkdownSection(content, "browser");
|
|
1910
|
-
content = removeMarkdownSection(content, "writing-style");
|
|
1967
|
+
const content = removeSystemPromptSections(prompt);
|
|
1911
1968
|
actions.push({ kind: "write", target: systemPromptFile, content });
|
|
1912
1969
|
}
|
|
1913
|
-
const configFile =
|
|
1970
|
+
const configFile = path11.join(ctx.configDir, "config.toml");
|
|
1914
1971
|
const config = readTextIfExists(configFile);
|
|
1915
1972
|
if (config !== null) {
|
|
1916
1973
|
let content = config;
|
|
@@ -1945,7 +2002,7 @@ args = [${(server.args ?? []).map(tomlString).join(", ")}]`);
|
|
|
1945
2002
|
...primaryModelOwnership.length > 0 ? { primaryModelOwnership } : {}
|
|
1946
2003
|
});
|
|
1947
2004
|
}
|
|
1948
|
-
const hooksFile =
|
|
2005
|
+
const hooksFile = path11.join(ctx.configDir, "hooks.json");
|
|
1949
2006
|
const hooksJson = readTextIfExists(hooksFile);
|
|
1950
2007
|
if (hooksJson !== null) {
|
|
1951
2008
|
const content = removeNativeHooks(hooksJson, hooks);
|
|
@@ -1958,8 +2015,8 @@ args = [${(server.args ?? []).map(tomlString).join(", ")}]`);
|
|
|
1958
2015
|
};
|
|
1959
2016
|
|
|
1960
2017
|
// src/lib/install-mode.ts
|
|
1961
|
-
import
|
|
1962
|
-
import
|
|
2018
|
+
import fs9 from "fs";
|
|
2019
|
+
import path12 from "path";
|
|
1963
2020
|
var INSTALL_MODES = /* @__PURE__ */ new Set(["human", "programmatic"]);
|
|
1964
2021
|
var SUBAGENT_CONCURRENCIES = /* @__PURE__ */ new Set(["serial", "parallel"]);
|
|
1965
2022
|
var DEFAULT_INSTALL_MODE_PREFERENCE = {
|
|
@@ -1967,10 +2024,10 @@ var DEFAULT_INSTALL_MODE_PREFERENCE = {
|
|
|
1967
2024
|
subagentConcurrency: "serial"
|
|
1968
2025
|
};
|
|
1969
2026
|
function installModePreferenceFile() {
|
|
1970
|
-
return
|
|
2027
|
+
return path12.join(dataDir(), "install-mode.json");
|
|
1971
2028
|
}
|
|
1972
2029
|
function hasInstallModePreference(file = installModePreferenceFile()) {
|
|
1973
|
-
return
|
|
2030
|
+
return fs9.existsSync(file);
|
|
1974
2031
|
}
|
|
1975
2032
|
function isInstallMode(value) {
|
|
1976
2033
|
return INSTALL_MODES.has(value);
|
|
@@ -2001,9 +2058,9 @@ function normalizeInstallModePreference(value) {
|
|
|
2001
2058
|
throw new Error("Preferencia de instalaci\xF3n inv\xE1lida o corrupta.");
|
|
2002
2059
|
}
|
|
2003
2060
|
function loadInstallModePreference(file = installModePreferenceFile()) {
|
|
2004
|
-
if (!
|
|
2061
|
+
if (!fs9.existsSync(file)) return DEFAULT_INSTALL_MODE_PREFERENCE;
|
|
2005
2062
|
try {
|
|
2006
|
-
const raw =
|
|
2063
|
+
const raw = fs9.readFileSync(file, "utf8");
|
|
2007
2064
|
return normalizeInstallModePreference(JSON.parse(raw));
|
|
2008
2065
|
} catch (error) {
|
|
2009
2066
|
const message = error instanceof Error ? error.message : String(error);
|
|
@@ -2041,10 +2098,10 @@ function parseInstallModePreferenceFlags(mode, subagentConcurrency) {
|
|
|
2041
2098
|
}
|
|
2042
2099
|
|
|
2043
2100
|
// src/lib/manifest.ts
|
|
2044
|
-
import
|
|
2045
|
-
import
|
|
2101
|
+
import fs10 from "fs";
|
|
2102
|
+
import path13 from "path";
|
|
2046
2103
|
function manifestFile() {
|
|
2047
|
-
return
|
|
2104
|
+
return path13.join(dataDir(), "manifest.json");
|
|
2048
2105
|
}
|
|
2049
2106
|
function readManifest(file = manifestFile()) {
|
|
2050
2107
|
const raw = readTextIfExists(file);
|
|
@@ -2068,24 +2125,24 @@ function removeRuntimeManifest(id, file = manifestFile()) {
|
|
|
2068
2125
|
writeText(file, JSON.stringify(manifest, null, 2) + "\n");
|
|
2069
2126
|
}
|
|
2070
2127
|
function findOrphans(prevOwned, currentTargets, root = HOME) {
|
|
2071
|
-
return prevOwned.map((f) =>
|
|
2072
|
-
(f) => !currentTargets.has(f) &&
|
|
2128
|
+
return prevOwned.map((f) => path13.resolve(f)).filter(
|
|
2129
|
+
(f) => !currentTargets.has(f) && path13.basename(f) !== "engram.ts" && isContainedIn(f, root) && fs10.existsSync(f)
|
|
2073
2130
|
);
|
|
2074
2131
|
}
|
|
2075
2132
|
|
|
2076
2133
|
// src/components/system-prompt.ts
|
|
2077
|
-
import
|
|
2078
|
-
import
|
|
2134
|
+
import path15 from "path";
|
|
2135
|
+
import fs12 from "fs";
|
|
2079
2136
|
|
|
2080
2137
|
// src/lib/mode-composition.ts
|
|
2081
|
-
import
|
|
2082
|
-
import
|
|
2138
|
+
import fs11 from "fs";
|
|
2139
|
+
import path14 from "path";
|
|
2083
2140
|
var PROGRAMMATIC_ROOT = ["modes", "programmatic"];
|
|
2084
2141
|
var PROGRAMMATIC_MARKER = "<!-- jorgex:programmatic-mode -->";
|
|
2085
2142
|
var LEGACY_RESULT_CONTRACT_SECTION = /\n?##\s+Result contract[\s\S]*$/;
|
|
2086
2143
|
var normalize = (value) => value.replace(/\r\n/g, "\n");
|
|
2087
2144
|
function loadProgrammaticAddendum(stackDir, fileName) {
|
|
2088
|
-
return normalize(
|
|
2145
|
+
return normalize(fs11.readFileSync(path14.join(stackDir, ...PROGRAMMATIC_ROOT, fileName), "utf8")).trim();
|
|
2089
2146
|
}
|
|
2090
2147
|
function appendAddendum(base, addendum) {
|
|
2091
2148
|
const normalizedBase = normalize(base);
|
|
@@ -2128,30 +2185,30 @@ function composeProgrammaticAgentBody(stackDir, agent, mode, concurrency) {
|
|
|
2128
2185
|
var normalize2 = (s) => s.replace(/\r\n/g, "\n");
|
|
2129
2186
|
function planSystemPrompt(adapter, ctx) {
|
|
2130
2187
|
const target = adapter.paths(ctx.configDir).systemPromptFile;
|
|
2131
|
-
const
|
|
2132
|
-
|
|
2133
|
-
|
|
2188
|
+
const existing = readSystemPromptFile(target);
|
|
2189
|
+
assertSystemPromptMarkers(existing, target);
|
|
2190
|
+
const readModule = (file) => stripLeadingHtmlComments(
|
|
2191
|
+
normalize2(fs12.readFileSync(path15.join(ctx.stackDir, "system-prompt", file), "utf8"))
|
|
2134
2192
|
);
|
|
2135
|
-
const
|
|
2136
|
-
|
|
2137
|
-
ctx
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
|
|
2145
|
-
|
|
2193
|
+
const modules = {
|
|
2194
|
+
"system-prompt": composeProgrammaticSystemPrompt(ctx.stackDir, readModule("AGENTS.md"), ctx.mode),
|
|
2195
|
+
"engram-protocol": adapter.injectEngramProtocol(ctx) ? readModule("engram-protocol.md") : void 0,
|
|
2196
|
+
context7: readModule("context7.md"),
|
|
2197
|
+
playwright: ctx.playwrightCliEnabled ? readModule("browser-playwright.md") : void 0,
|
|
2198
|
+
"chrome-devtools": ctx.enabledMcpServers?.has(DEVTOOLS_MCP_SERVER) ? readModule("browser-chrome-devtools.md") : void 0,
|
|
2199
|
+
"writing-style": ctx.mode === "programmatic" ? void 0 : ctx.writingStyle?.content ?? void 0
|
|
2200
|
+
};
|
|
2201
|
+
const sections = adapter.adaptSystemPromptSections?.(modules) ?? modules;
|
|
2202
|
+
let content = existing ?? "";
|
|
2203
|
+
for (const section of SYSTEM_PROMPT_SECTIONS) {
|
|
2204
|
+
const body = sections[section];
|
|
2205
|
+
content = body ? upsertMarkdownSection(content, section, body) : removeMarkdownSection(content, section);
|
|
2146
2206
|
}
|
|
2147
|
-
content = browser === "" ? removeMarkdownSection(content, "browser") : upsertMarkdownSection(content, "browser", browser);
|
|
2148
|
-
const style = ctx.mode === "programmatic" ? null : ctx.writingStyle?.content;
|
|
2149
|
-
content = style ? upsertMarkdownSection(content, "writing-style", style) : removeMarkdownSection(content, "writing-style");
|
|
2150
2207
|
return [{ kind: "write", target, content }];
|
|
2151
2208
|
}
|
|
2152
2209
|
|
|
2153
2210
|
// src/components/agents.ts
|
|
2154
|
-
import
|
|
2211
|
+
import path16 from "path";
|
|
2155
2212
|
function planAgents(adapter, ctx) {
|
|
2156
2213
|
const { agentsDir, commandsDir, outputStylesDir, profilesDir, scriptsDir } = adapter.paths(ctx.configDir);
|
|
2157
2214
|
const dirFor = {
|
|
@@ -2161,7 +2218,7 @@ function planAgents(adapter, ctx) {
|
|
|
2161
2218
|
profile: profilesDir
|
|
2162
2219
|
};
|
|
2163
2220
|
const scriptsBase = scriptsDir.replace(/\\/g, "/");
|
|
2164
|
-
return loadCanonicalAgents(
|
|
2221
|
+
return loadCanonicalAgents(path16.join(ctx.stackDir, "agents")).flatMap((agent) => {
|
|
2165
2222
|
const composedAgent = {
|
|
2166
2223
|
...agent,
|
|
2167
2224
|
body: composeProgrammaticAgentBody(ctx.stackDir, agent, ctx.mode, ctx.subagentConcurrency)
|
|
@@ -2173,42 +2230,42 @@ function planAgents(adapter, ctx) {
|
|
|
2173
2230
|
return [];
|
|
2174
2231
|
}
|
|
2175
2232
|
const content = rendered.content.replace(/\{\{SCRIPTS_DIR\}\}/g, scriptsBase);
|
|
2176
|
-
return [{ kind: "write", target:
|
|
2233
|
+
return [{ kind: "write", target: path16.join(dir, rendered.file), content }];
|
|
2177
2234
|
});
|
|
2178
2235
|
});
|
|
2179
2236
|
}
|
|
2180
2237
|
|
|
2181
2238
|
// src/components/skills.ts
|
|
2182
|
-
import
|
|
2239
|
+
import path17 from "path";
|
|
2183
2240
|
function planSkills(adapter, ctx) {
|
|
2184
2241
|
const { skillsDir } = adapter.paths(ctx.configDir);
|
|
2185
|
-
const source =
|
|
2242
|
+
const source = path17.join(ctx.stackDir, "skills");
|
|
2186
2243
|
return listFilesRecursive(source).map((file) => {
|
|
2187
|
-
const relative =
|
|
2188
|
-
return { kind: "copy", source: file, target:
|
|
2244
|
+
const relative = path17.relative(source, file);
|
|
2245
|
+
return { kind: "copy", source: file, target: path17.join(skillsDir, relative) };
|
|
2189
2246
|
});
|
|
2190
2247
|
}
|
|
2191
2248
|
|
|
2192
2249
|
// src/components/commands.ts
|
|
2193
|
-
import
|
|
2194
|
-
import
|
|
2250
|
+
import path18 from "path";
|
|
2251
|
+
import fs13 from "fs";
|
|
2195
2252
|
function planCommands(adapter, ctx) {
|
|
2196
2253
|
const { commandsDir } = adapter.paths(ctx.configDir);
|
|
2197
|
-
const source =
|
|
2198
|
-
if (!
|
|
2254
|
+
const source = path18.join(ctx.stackDir, "commands");
|
|
2255
|
+
if (!fs13.existsSync(source)) return [];
|
|
2199
2256
|
const commandFiles = [
|
|
2200
2257
|
...listMarkdownFiles(source),
|
|
2201
|
-
...listMarkdownFiles(
|
|
2258
|
+
...listMarkdownFiles(path18.join(source, adapter.id))
|
|
2202
2259
|
];
|
|
2203
2260
|
return commandFiles.map(({ file, fullPath }) => {
|
|
2204
|
-
const raw =
|
|
2261
|
+
const raw = fs13.readFileSync(fullPath, "utf8").replace(/\r\n/g, "\n");
|
|
2205
2262
|
const rendered = adapter.renderCommand(file, raw);
|
|
2206
|
-
return { kind: "write", target:
|
|
2263
|
+
return { kind: "write", target: path18.join(commandsDir, rendered.file), content: rendered.content };
|
|
2207
2264
|
});
|
|
2208
2265
|
}
|
|
2209
2266
|
function listMarkdownFiles(dir) {
|
|
2210
|
-
if (!
|
|
2211
|
-
return
|
|
2267
|
+
if (!fs13.existsSync(dir)) return [];
|
|
2268
|
+
return fs13.readdirSync(dir, { withFileTypes: true }).filter((entry) => entry.isFile() && entry.name.endsWith(".md")).map((entry) => ({ file: entry.name, fullPath: path18.join(dir, entry.name) }));
|
|
2212
2269
|
}
|
|
2213
2270
|
|
|
2214
2271
|
// src/components/hooks.ts
|
|
@@ -2222,23 +2279,23 @@ function planMcp(adapter, ctx) {
|
|
|
2222
2279
|
}
|
|
2223
2280
|
|
|
2224
2281
|
// src/components/plugins.ts
|
|
2225
|
-
import
|
|
2226
|
-
import
|
|
2282
|
+
import path19 from "path";
|
|
2283
|
+
import fs14 from "fs";
|
|
2227
2284
|
function planPlugins(adapter, ctx) {
|
|
2228
2285
|
const { pluginsDir } = adapter.paths(ctx.configDir);
|
|
2229
2286
|
if (pluginsDir === null) return [];
|
|
2230
|
-
const source =
|
|
2231
|
-
if (!
|
|
2287
|
+
const source = path19.join(ctx.stackDir, "plugins", adapter.id);
|
|
2288
|
+
if (!fs14.existsSync(source)) return [];
|
|
2232
2289
|
return listFilesRecursive(source).filter((f) => f.endsWith(".ts")).map((sourceFile) => {
|
|
2233
|
-
const target =
|
|
2234
|
-
const raw =
|
|
2290
|
+
const target = path19.join(pluginsDir, path19.relative(source, sourceFile));
|
|
2291
|
+
const raw = fs14.readFileSync(sourceFile, "utf8");
|
|
2235
2292
|
let content = raw;
|
|
2236
2293
|
if (content.includes('"{{ENGRAM_BIN}}"')) {
|
|
2237
2294
|
content = content.replace(/"\{\{ENGRAM_BIN\}\}"/g, JSON.stringify(ctx.engramBin ?? "engram"));
|
|
2238
2295
|
}
|
|
2239
2296
|
if (content.includes('"{{ENGRAM_PROTOCOL}}"')) {
|
|
2240
2297
|
const protocol = stripLeadingHtmlComments(
|
|
2241
|
-
|
|
2298
|
+
fs14.readFileSync(path19.join(ctx.stackDir, "system-prompt", "engram-protocol.md"), "utf8").replace(/\r\n/g, "\n")
|
|
2242
2299
|
);
|
|
2243
2300
|
content = content.replace(/"\{\{ENGRAM_PROTOCOL\}\}"/g, JSON.stringify(protocol));
|
|
2244
2301
|
}
|
|
@@ -2249,9 +2306,9 @@ function planPlugins(adapter, ctx) {
|
|
|
2249
2306
|
}
|
|
2250
2307
|
|
|
2251
2308
|
// src/lib/external-tools.ts
|
|
2252
|
-
import
|
|
2309
|
+
import fs15 from "fs";
|
|
2253
2310
|
import os from "os";
|
|
2254
|
-
import
|
|
2311
|
+
import path20 from "path";
|
|
2255
2312
|
import { execFileSync as execFileSync2 } from "child_process";
|
|
2256
2313
|
var PLAYWRIGHT_CLI = {
|
|
2257
2314
|
packageName: "@playwright/cli",
|
|
@@ -2291,14 +2348,15 @@ function resolvePlaywrightCliState({ binPath, versionOutput }) {
|
|
|
2291
2348
|
detectedVersion
|
|
2292
2349
|
};
|
|
2293
2350
|
}
|
|
2294
|
-
function detectPlaywrightCli() {
|
|
2295
|
-
const
|
|
2351
|
+
function detectPlaywrightCli(env) {
|
|
2352
|
+
const actualEnv = env ?? process.env;
|
|
2353
|
+
const binPath = env === void 0 ? lookPath(PLAYWRIGHT_CLI.bin) : lookPath(PLAYWRIGHT_CLI.bin, env);
|
|
2296
2354
|
if (binPath === null) {
|
|
2297
|
-
const pnpmHome = resolvePnpmHome();
|
|
2355
|
+
const pnpmHome = resolvePnpmHome(actualEnv);
|
|
2298
2356
|
const name = process.platform === "win32" ? `${PLAYWRIGHT_CLI.bin}.cmd` : PLAYWRIGHT_CLI.bin;
|
|
2299
|
-
const known = [
|
|
2357
|
+
const known = [path20.join(pnpmHome, name), path20.join(pnpmHome, "bin", name)].find((file) => {
|
|
2300
2358
|
try {
|
|
2301
|
-
return
|
|
2359
|
+
return fs15.statSync(file).isFile();
|
|
2302
2360
|
} catch {
|
|
2303
2361
|
return false;
|
|
2304
2362
|
}
|
|
@@ -2307,17 +2365,17 @@ function detectPlaywrightCli() {
|
|
|
2307
2365
|
}
|
|
2308
2366
|
return resolvePlaywrightCliState({
|
|
2309
2367
|
binPath,
|
|
2310
|
-
versionOutput: binPath ? runDetectedBin(binPath, ["--version"], 5e3, { NO_UPDATE_NOTIFIER: "1" }) : null
|
|
2368
|
+
versionOutput: binPath ? runDetectedBin(binPath, ["--version"], 5e3, { ...actualEnv, NO_UPDATE_NOTIFIER: "1" }) : null
|
|
2311
2369
|
});
|
|
2312
2370
|
}
|
|
2313
|
-
function resolvePnpmBin() {
|
|
2314
|
-
const pnpm = lookPath("pnpm");
|
|
2371
|
+
function resolvePnpmBin(env) {
|
|
2372
|
+
const pnpm = env === void 0 ? lookPath("pnpm") : lookPath("pnpm", env);
|
|
2315
2373
|
if (pnpm !== null && !pnpm.toLowerCase().endsWith(".ps1")) return pnpm;
|
|
2316
|
-
const pnpmCmd = lookPath("pnpm.cmd");
|
|
2374
|
+
const pnpmCmd = env === void 0 ? lookPath("pnpm.cmd") : lookPath("pnpm.cmd", env);
|
|
2317
2375
|
return pnpmCmd !== null && !pnpmCmd.toLowerCase().endsWith(".ps1") ? pnpmCmd : null;
|
|
2318
2376
|
}
|
|
2319
2377
|
function resolvePnpmHome(env = process.env, platform = process.platform, homeDir = os.homedir()) {
|
|
2320
|
-
const paths = platform === "win32" ?
|
|
2378
|
+
const paths = platform === "win32" ? path20.win32 : path20.posix;
|
|
2321
2379
|
if (env.PNPM_HOME) return env.PNPM_HOME;
|
|
2322
2380
|
if (env.XDG_DATA_HOME) return paths.join(env.XDG_DATA_HOME, "pnpm");
|
|
2323
2381
|
if (platform === "darwin") return paths.join(homeDir, "Library", "pnpm");
|
|
@@ -2326,7 +2384,7 @@ function resolvePnpmHome(env = process.env, platform = process.platform, homeDir
|
|
|
2326
2384
|
}
|
|
2327
2385
|
function setupPnpmGlobal(pnpmBin, env = process.env, platform = process.platform, homeDir = os.homedir()) {
|
|
2328
2386
|
const pnpmHome = resolvePnpmHome(env, platform, homeDir);
|
|
2329
|
-
const paths = platform === "win32" ?
|
|
2387
|
+
const paths = platform === "win32" ? path20.win32 : path20.posix;
|
|
2330
2388
|
if (!paths.isAbsolute(pnpmHome)) return { ok: false, reason: "PNPM_HOME debe ser una ruta absoluta; corrige la configuraci\xF3n antes de reintentar." };
|
|
2331
2389
|
const pathEntries = [pnpmHome, paths.join(pnpmHome, "bin")];
|
|
2332
2390
|
if (env.PATH !== void 0) pathEntries.push(env.PATH);
|
|
@@ -2346,10 +2404,10 @@ function setupPnpmGlobal(pnpmBin, env = process.env, platform = process.platform
|
|
|
2346
2404
|
}
|
|
2347
2405
|
function isPlaywrightBrowserReady(env = process.env, platform = process.platform, homeDir = os.homedir()) {
|
|
2348
2406
|
const configuredPath = env.PLAYWRIGHT_BROWSERS_PATH;
|
|
2349
|
-
const cacheDir = configuredPath ?? (platform === "win32" ?
|
|
2407
|
+
const cacheDir = configuredPath ?? (platform === "win32" ? path20.join(env.LOCALAPPDATA ?? path20.join(homeDir, "AppData", "Local"), "ms-playwright") : platform === "darwin" ? path20.join(homeDir, "Library", "Caches", "ms-playwright") : path20.join(env.XDG_CACHE_HOME ?? path20.join(homeDir, ".cache"), "ms-playwright"));
|
|
2350
2408
|
if (configuredPath === "0") return { status: "missing", path: cacheDir, errorCode: "DISABLED" };
|
|
2351
2409
|
try {
|
|
2352
|
-
const ready =
|
|
2410
|
+
const ready = fs15.readdirSync(cacheDir, { withFileTypes: true }).some(
|
|
2353
2411
|
(entry) => entry.isDirectory() && /^chromium(?:_headless_shell)?-/.test(entry.name)
|
|
2354
2412
|
);
|
|
2355
2413
|
return ready ? { status: "ready", path: cacheDir } : { status: "missing", path: cacheDir };
|
|
@@ -2381,9 +2439,9 @@ function verifyPlaywrightBrowser(pnpmBin, env = process.env, cwd) {
|
|
|
2381
2439
|
cwd,
|
|
2382
2440
|
stdio: ["ignore", "pipe", "inherit"]
|
|
2383
2441
|
}).trim();
|
|
2384
|
-
if (!
|
|
2385
|
-
const packageFile =
|
|
2386
|
-
const installed = JSON.parse(
|
|
2442
|
+
if (!path20.isAbsolute(root)) return false;
|
|
2443
|
+
const packageFile = fs15.realpathSync(path20.join(root, "@playwright", "cli", "package.json"));
|
|
2444
|
+
const installed = JSON.parse(fs15.readFileSync(packageFile, "utf8"));
|
|
2387
2445
|
if (installed.name !== PLAYWRIGHT_CLI.packageName || installed.version !== PLAYWRIGHT_CLI.version) return false;
|
|
2388
2446
|
const probe = `
|
|
2389
2447
|
const { createRequire } = require('node:module');
|
|
@@ -2426,7 +2484,7 @@ function executePlaywrightToolAction(action, pnpmBin = resolvePnpmBin(), env) {
|
|
|
2426
2484
|
const command = planPlaywrightCliCommand(action, pnpmBin);
|
|
2427
2485
|
const invocation = planDetectedBinCommand(command.command, command.args);
|
|
2428
2486
|
if (invocation === null) return { ok: false, reason: "pnpm-command" };
|
|
2429
|
-
const cwd = action === "install-browser" ?
|
|
2487
|
+
const cwd = action === "install-browser" ? fs15.mkdtempSync(path20.join(os.tmpdir(), "jorgex-playwright-")) : void 0;
|
|
2430
2488
|
const childEnv = action === "install-browser" ? { ...env ?? process.env, NO_UPDATE_NOTIFIER: "1" } : env ?? process.env;
|
|
2431
2489
|
try {
|
|
2432
2490
|
execFileSync2(invocation.command, invocation.args, {
|
|
@@ -2441,27 +2499,51 @@ function executePlaywrightToolAction(action, pnpmBin = resolvePnpmBin(), env) {
|
|
|
2441
2499
|
} catch {
|
|
2442
2500
|
return { ok: false, reason: "action-failed" };
|
|
2443
2501
|
} finally {
|
|
2444
|
-
if (cwd !== void 0)
|
|
2502
|
+
if (cwd !== void 0) fs15.rmSync(cwd, { recursive: true, force: true });
|
|
2503
|
+
}
|
|
2504
|
+
}
|
|
2505
|
+
|
|
2506
|
+
// src/lib/playwright-capability.ts
|
|
2507
|
+
function inspectPlaywrightCapability(options = {}) {
|
|
2508
|
+
const env = options.env ?? process.env;
|
|
2509
|
+
const cli = options.env === void 0 ? detectPlaywrightCli() : detectPlaywrightCli(env);
|
|
2510
|
+
const browserCache = options.env === void 0 ? isPlaywrightBrowserReady() : isPlaywrightBrowserReady(env);
|
|
2511
|
+
let browserVerified = false;
|
|
2512
|
+
if (cli.status === "current" && browserCache.status === "ready") {
|
|
2513
|
+
if (options.browserVerified === true) {
|
|
2514
|
+
browserVerified = true;
|
|
2515
|
+
} else {
|
|
2516
|
+
const pnpmBin = options.env === void 0 ? resolvePnpmBin() : resolvePnpmBin(env);
|
|
2517
|
+
if (pnpmBin !== null) {
|
|
2518
|
+
try {
|
|
2519
|
+
browserVerified = verifyPlaywrightBrowser(pnpmBin, env);
|
|
2520
|
+
} catch {
|
|
2521
|
+
browserVerified = false;
|
|
2522
|
+
}
|
|
2523
|
+
}
|
|
2524
|
+
}
|
|
2445
2525
|
}
|
|
2526
|
+
const effective = cli.status === "current" && cli.binPath !== null && (options.browserVerified === true || browserCache.status === "ready") && browserVerified;
|
|
2527
|
+
return { cli, browserCache, browserVerified, effective };
|
|
2446
2528
|
}
|
|
2447
2529
|
|
|
2448
2530
|
// src/lib/tool-preferences.ts
|
|
2449
|
-
import
|
|
2450
|
-
import
|
|
2531
|
+
import fs16 from "fs";
|
|
2532
|
+
import path21 from "path";
|
|
2451
2533
|
var PLAYWRIGHT_CLI_PREFERENCE_VERSION = 1;
|
|
2452
2534
|
var DEVTOOLS_MCP_PREFERENCE_VERSION = 1;
|
|
2453
2535
|
var PRIMARY_MODEL_OWNERSHIP_VERSION = 1;
|
|
2454
2536
|
var PLAYWRIGHT_RUNTIMES = ["opencode", "claude-code", "codex", "pi"];
|
|
2455
2537
|
function readPreference(file) {
|
|
2456
2538
|
try {
|
|
2457
|
-
return { raw:
|
|
2539
|
+
return { raw: fs16.readFileSync(file, "utf8"), errorCode: null };
|
|
2458
2540
|
} catch (error) {
|
|
2459
2541
|
const code = error instanceof Error && "code" in error && typeof error.code === "string" ? error.code : "UNKNOWN";
|
|
2460
2542
|
return code === "ENOENT" ? { raw: null, errorCode: null } : { raw: null, errorCode: code };
|
|
2461
2543
|
}
|
|
2462
2544
|
}
|
|
2463
2545
|
function playwrightCliPreferenceFile(stateDir = dataDir()) {
|
|
2464
|
-
return
|
|
2546
|
+
return path21.join(stateDir, "playwright-cli.json");
|
|
2465
2547
|
}
|
|
2466
2548
|
function parsePlaywrightCliPreference(raw) {
|
|
2467
2549
|
try {
|
|
@@ -2507,10 +2589,10 @@ function savePlaywrightCliPreference(file, enabled, selection) {
|
|
|
2507
2589
|
writeText(file, content);
|
|
2508
2590
|
}
|
|
2509
2591
|
function devtoolsMcpPreferenceFile(stateDir = dataDir()) {
|
|
2510
|
-
return
|
|
2592
|
+
return path21.join(stateDir, "devtools-mcp.json");
|
|
2511
2593
|
}
|
|
2512
2594
|
function primaryModelOwnershipFile(stateDir = dataDir()) {
|
|
2513
|
-
return
|
|
2595
|
+
return path21.join(stateDir, "primary-model.json");
|
|
2514
2596
|
}
|
|
2515
2597
|
function isRecord2(value) {
|
|
2516
2598
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
@@ -2620,7 +2702,7 @@ function primaryModelOwnershipError(file = primaryModelOwnershipFile()) {
|
|
|
2620
2702
|
return `Primary model: ownership inv\xE1lido en ${file}. Corrige o borra ese archivo antes de reintentar.`;
|
|
2621
2703
|
}
|
|
2622
2704
|
function primaryModelConfigKey(configDir) {
|
|
2623
|
-
const resolved =
|
|
2705
|
+
const resolved = path21.resolve(configDir);
|
|
2624
2706
|
return process.platform === "win32" ? resolved.toLowerCase() : resolved;
|
|
2625
2707
|
}
|
|
2626
2708
|
function loadPrimaryModelOwnership(file, runtime, configDir) {
|
|
@@ -2705,7 +2787,7 @@ function persistConfigurationOwnershipChanges(runtime, configDir, plan) {
|
|
|
2705
2787
|
const primaryFile = primaryModelOwnershipFile();
|
|
2706
2788
|
for (const [field, owned] of primary) savePrimaryModelOwnership(primaryFile, runtime, configDir, field, owned);
|
|
2707
2789
|
}
|
|
2708
|
-
function makeContext(adapter, configDir, mode = DEFAULT_INSTALL_MODE_PREFERENCE, useBrowserPreferences = true) {
|
|
2790
|
+
function makeContext(adapter, configDir, mode = DEFAULT_INSTALL_MODE_PREFERENCE, useBrowserPreferences = true, playwrightCapability) {
|
|
2709
2791
|
const models = loadModelMap()[adapter.id];
|
|
2710
2792
|
if (!models) return null;
|
|
2711
2793
|
return {
|
|
@@ -2717,7 +2799,7 @@ function makeContext(adapter, configDir, mode = DEFAULT_INSTALL_MODE_PREFERENCE,
|
|
|
2717
2799
|
models,
|
|
2718
2800
|
warnings: [],
|
|
2719
2801
|
enabledMcpServers: enabledMcpServers(adapter.id, void 0, useBrowserPreferences),
|
|
2720
|
-
playwrightCliEnabled: useBrowserPreferences && loadPlaywrightCliPreference(playwrightCliPreferenceFile(), adapter.id) === true,
|
|
2802
|
+
playwrightCliEnabled: useBrowserPreferences && loadPlaywrightCliPreference(playwrightCliPreferenceFile(), adapter.id) === true && (playwrightCapability ?? true),
|
|
2721
2803
|
ownedMcpServers: ownedMcpServers(adapter.id, useBrowserPreferences),
|
|
2722
2804
|
ownedPrimaryModelFields: useBrowserPreferences ? loadPrimaryModelOwnership(primaryModelOwnershipFile(), adapter.id, configDir) : /* @__PURE__ */ new Set()
|
|
2723
2805
|
};
|
|
@@ -2740,7 +2822,7 @@ function diffPlan(plan) {
|
|
|
2740
2822
|
if (current === null) return { action, status: "create" };
|
|
2741
2823
|
return { action, status: current === action.content ? "unchanged" : "update" };
|
|
2742
2824
|
}
|
|
2743
|
-
if (!
|
|
2825
|
+
if (!fs17.existsSync(action.target)) return { action, status: "create" };
|
|
2744
2826
|
return { action, status: sameFileContent(action.source, action.target) ? "unchanged" : "update" };
|
|
2745
2827
|
});
|
|
2746
2828
|
}
|
|
@@ -2752,21 +2834,21 @@ function applyChanges(changes, onOwnershipWritten) {
|
|
|
2752
2834
|
} else copyFile(action.source, action.target);
|
|
2753
2835
|
}
|
|
2754
2836
|
}
|
|
2755
|
-
function collectAllCurrentTargets(mode = DEFAULT_INSTALL_MODE_PREFERENCE) {
|
|
2837
|
+
function collectAllCurrentTargets(mode = DEFAULT_INSTALL_MODE_PREFERENCE, playwrightCapability) {
|
|
2756
2838
|
const targets = /* @__PURE__ */ new Set();
|
|
2757
2839
|
let complete = true;
|
|
2758
2840
|
const warnings = [];
|
|
2759
2841
|
for (const adapter of Object.values(ADAPTERS)) {
|
|
2760
2842
|
const detection = adapter.detect();
|
|
2761
2843
|
if (!detection.installed) continue;
|
|
2762
|
-
const ctx = makeContext(adapter, detection.configDir, mode);
|
|
2844
|
+
const ctx = makeContext(adapter, detection.configDir, mode, true, playwrightCapability);
|
|
2763
2845
|
if (!ctx) {
|
|
2764
2846
|
complete = false;
|
|
2765
2847
|
warnings.push(`${adapter.name}: limpieza de hu\xE9rfanos deshabilitada \u2014 falta contexto/model-map instalable para este runtime.`);
|
|
2766
2848
|
continue;
|
|
2767
2849
|
}
|
|
2768
2850
|
try {
|
|
2769
|
-
for (const action of buildPlan(adapter, ctx)) targets.add(
|
|
2851
|
+
for (const action of buildPlan(adapter, ctx)) targets.add(path22.resolve(action.target));
|
|
2770
2852
|
} catch (error) {
|
|
2771
2853
|
complete = false;
|
|
2772
2854
|
warnings.push(
|
|
@@ -2779,6 +2861,18 @@ function collectAllCurrentTargets(mode = DEFAULT_INSTALL_MODE_PREFERENCE) {
|
|
|
2779
2861
|
async function runInstall(opts) {
|
|
2780
2862
|
const showSummary = opts.showSummary !== false;
|
|
2781
2863
|
if (showSummary) p.intro(`jorgex-stack ${opts.dryRun ? "install (dry-run)" : "install"}`);
|
|
2864
|
+
try {
|
|
2865
|
+
for (const id of opts.runtimes) {
|
|
2866
|
+
const adapter = ADAPTERS[id];
|
|
2867
|
+
if (!adapter) continue;
|
|
2868
|
+
const detection = adapter.detect();
|
|
2869
|
+
if (opts.targetDir === void 0 && !detection.installed) continue;
|
|
2870
|
+
assertSystemPromptFile(adapter.paths(opts.targetDir ?? detection.configDir).systemPromptFile, opts.targetDir);
|
|
2871
|
+
}
|
|
2872
|
+
} catch (error) {
|
|
2873
|
+
p.log.error(error instanceof Error ? error.message : String(error));
|
|
2874
|
+
return 1;
|
|
2875
|
+
}
|
|
2782
2876
|
let writingStyle;
|
|
2783
2877
|
let preparedStyle;
|
|
2784
2878
|
try {
|
|
@@ -2804,6 +2898,10 @@ async function runInstall(opts) {
|
|
|
2804
2898
|
});
|
|
2805
2899
|
const projectPlaywrightPrompt = opts.dryRun && toolPlan?.persistEnabledOnSuccess === true;
|
|
2806
2900
|
const hasFileRuntimes = opts.runtimes.length > 0;
|
|
2901
|
+
const shouldInspectPlaywright = useManifest && !opts.dryRun && (toolPlan === null || toolPlan.actions.length === 0) && loadPlaywrightCliPreference() === true;
|
|
2902
|
+
const playwrightCapability = opts.dryRun || !useManifest ? void 0 : opts.playwrightCapability ?? (shouldInspectPlaywright ? inspectPlaywrightCapability() : void 0);
|
|
2903
|
+
const effectivePlaywright = playwrightCapability?.effective;
|
|
2904
|
+
const plannedPlaywright = effectivePlaywright ?? (toolPlan !== null && toolPlan.actions.length > 0 ? false : void 0);
|
|
2807
2905
|
const modelMap = hasFileRuntimes ? loadModelMap() : {};
|
|
2808
2906
|
if (preparedStyle !== void 0) {
|
|
2809
2907
|
try {
|
|
@@ -2816,7 +2914,7 @@ async function runInstall(opts) {
|
|
|
2816
2914
|
}
|
|
2817
2915
|
if (hasFileRuntimes && useManifest && !opts.dryRun) ensureModelMapFile();
|
|
2818
2916
|
p.log.info(engramBin ? `Engram detectado: ${engramBin} (se respeta, D7)` : "Engram NO detectado.");
|
|
2819
|
-
const current = hasFileRuntimes && useManifest ? collectAllCurrentTargets(modePreference) : { targets: /* @__PURE__ */ new Set(), complete: false, warnings: [] };
|
|
2917
|
+
const current = hasFileRuntimes && useManifest ? collectAllCurrentTargets(modePreference, plannedPlaywright) : { targets: /* @__PURE__ */ new Set(), complete: false, warnings: [] };
|
|
2820
2918
|
const canOrphan = hasFileRuntimes && useManifest && current.complete;
|
|
2821
2919
|
const canonicalMcp = loadCanonicalMcp(stackDir);
|
|
2822
2920
|
const canonicalHooks = loadCanonicalHooks(stackDir);
|
|
@@ -2855,7 +2953,7 @@ async function runInstall(opts) {
|
|
|
2855
2953
|
models,
|
|
2856
2954
|
warnings: [],
|
|
2857
2955
|
enabledMcpServers: enabledMcpServers(id, opts.devtoolsMcpSelection?.[id], useManifest),
|
|
2858
|
-
playwrightCliEnabled: projectPlaywrightPrompt ? opts.playwrightToolConsent?.runtimeSelection?.[id] ?? true : useManifest && loadPlaywrightCliPreference(playwrightCliPreferenceFile(), id) === true,
|
|
2956
|
+
playwrightCliEnabled: projectPlaywrightPrompt ? opts.playwrightToolConsent?.runtimeSelection?.[id] ?? true : useManifest && loadPlaywrightCliPreference(playwrightCliPreferenceFile(), id) === true && (plannedPlaywright ?? true),
|
|
2859
2957
|
ownedMcpServers: ownedMcpServers(id, useManifest),
|
|
2860
2958
|
ownedPrimaryModelFields: useManifest ? loadPrimaryModelOwnership(primaryModelOwnershipFile(), id, configDir) : /* @__PURE__ */ new Set()
|
|
2861
2959
|
};
|
|
@@ -2889,10 +2987,10 @@ async function runInstall(opts) {
|
|
|
2889
2987
|
}
|
|
2890
2988
|
const writeManifest = () => {
|
|
2891
2989
|
if (!useManifest) return;
|
|
2892
|
-
const unmergeTargets = new Set(adapter.planUnmerge(canonicalMcp, canonicalHooks, ctx).map((a) =>
|
|
2990
|
+
const unmergeTargets = new Set(adapter.planUnmerge(canonicalMcp, canonicalHooks, ctx).map((a) => path22.resolve(a.target)));
|
|
2893
2991
|
const keepTarget = (target) => !unmergeTargets.has(target);
|
|
2894
|
-
const liveOwned = plan.map((a) =>
|
|
2895
|
-
const previousOwned = (prevManifest?.owned ?? []).map((target) =>
|
|
2992
|
+
const liveOwned = plan.map((a) => path22.resolve(a.target)).filter(keepTarget);
|
|
2993
|
+
const previousOwned = (prevManifest?.owned ?? []).map((target) => path22.resolve(target)).filter(keepTarget);
|
|
2896
2994
|
const owned = canOrphan ? liveOwned : [.../* @__PURE__ */ new Set([...previousOwned, ...liveOwned])];
|
|
2897
2995
|
writeRuntimeManifest(id, { configDir, owned, updatedAt: (/* @__PURE__ */ new Date()).toISOString() });
|
|
2898
2996
|
};
|
|
@@ -2921,9 +3019,9 @@ async function runInstall(opts) {
|
|
|
2921
3019
|
const backup = useManifest ? createBackup([...updates.map((c) => c.action.target), ...orphans], `install-${id}`) : null;
|
|
2922
3020
|
if (backup) p.log.info(`Backup: ${backup.id} (${backup.files.length} archivos)`);
|
|
2923
3021
|
applyChanges(changes, useManifest ? (action) => persistConfigurationOwnershipChanges(id, configDir, [action]) : void 0);
|
|
2924
|
-
const pruneRoot = useManifest ? HOME :
|
|
3022
|
+
const pruneRoot = useManifest ? HOME : path22.dirname(configDir);
|
|
2925
3023
|
for (const orphan of orphans) {
|
|
2926
|
-
|
|
3024
|
+
fs17.rmSync(orphan, { force: true });
|
|
2927
3025
|
pruneEmptyDirs(orphan, pruneRoot);
|
|
2928
3026
|
}
|
|
2929
3027
|
const verifyCtx = { ...ctx, warnings: [] };
|
|
@@ -3021,21 +3119,23 @@ async function runInstall(opts) {
|
|
|
3021
3119
|
exitCode = 1;
|
|
3022
3120
|
p.log.error("Playwright CLI y navegador se han instalado y la preferencia est\xE1 activada, pero la gu\xEDa de navegador qued\xF3 en estado parcial. Ejecuta 'jorgex-stack sync' para repararla.");
|
|
3023
3121
|
} else {
|
|
3122
|
+
const verified = preparedEnv === void 0 ? inspectPlaywrightCapability({ browserVerified: true }) : inspectPlaywrightCapability({ browserVerified: true, env: preparedEnv });
|
|
3123
|
+
if (verified.effective && verified.cli.status === "current" && verified.cli.binPath !== null && verified.cli.detectedVersion !== null && verified.browserCache.status === "ready") {
|
|
3124
|
+
opts.onPlaywrightCapability?.(verified);
|
|
3125
|
+
}
|
|
3024
3126
|
p.log.success("Playwright CLI instalado y arranque de Chromium verificado.");
|
|
3025
3127
|
}
|
|
3026
3128
|
}
|
|
3027
3129
|
}
|
|
3028
|
-
} else if (
|
|
3029
|
-
|
|
3030
|
-
if (cli.status !== "current") {
|
|
3130
|
+
} else if (playwrightCapability !== void 0 && !playwrightCapability.effective) {
|
|
3131
|
+
if (playwrightCapability.cli.status !== "current") {
|
|
3031
3132
|
p.log.warn("Playwright CLI sigue habilitado, pero el paquete no est\xE1 listo; sync no instala herramientas. Ejecuta 'jorgex-stack install --playwright'.");
|
|
3133
|
+
} else if (playwrightCapability.browserCache.status === "unreadable") {
|
|
3134
|
+
p.log.warn(`Playwright CLI sigue habilitado, pero no se puede leer la cach\xE9 de navegadores en ${playwrightCapability.browserCache.path} (${playwrightCapability.browserCache.errorCode}). Revisa permisos o ejecuta 'jorgex-stack install --playwright'.`);
|
|
3135
|
+
} else if (playwrightCapability.browserCache.status === "missing") {
|
|
3136
|
+
p.log.warn("Playwright CLI sigue habilitado, pero falta el navegador; sync no descarga navegadores. Ejecuta 'jorgex-stack install --playwright'.");
|
|
3032
3137
|
} else {
|
|
3033
|
-
|
|
3034
|
-
if (browserCache.status === "unreadable") {
|
|
3035
|
-
p.log.warn(`Playwright CLI sigue habilitado, pero no se puede leer la cach\xE9 de navegadores en ${browserCache.path} (${browserCache.errorCode}). Revisa permisos o ejecuta 'jorgex-stack install --playwright'.`);
|
|
3036
|
-
} else if (browserCache.status === "missing") {
|
|
3037
|
-
p.log.warn("Playwright CLI sigue habilitado, pero falta el navegador; sync no descarga navegadores. Ejecuta 'jorgex-stack install --playwright'.");
|
|
3038
|
-
}
|
|
3138
|
+
p.log.warn("Playwright CLI sigue habilitado, pero Chromium no arranca; se ha retirado la gu\xEDa. Ejecuta 'jorgex-stack install --playwright' para repararlo.");
|
|
3039
3139
|
}
|
|
3040
3140
|
}
|
|
3041
3141
|
if (useManifest && !opts.dryRun && exitCode === 0 && successfulRuns > 0) {
|
|
@@ -3048,29 +3148,33 @@ async function runInstall(opts) {
|
|
|
3048
3148
|
}
|
|
3049
3149
|
|
|
3050
3150
|
// src/uninstall.ts
|
|
3051
|
-
import
|
|
3052
|
-
import
|
|
3151
|
+
import fs19 from "fs";
|
|
3152
|
+
import path26 from "path";
|
|
3053
3153
|
import * as p2 from "@clack/prompts";
|
|
3054
3154
|
|
|
3055
3155
|
// src/lib/pi-projection-lifecycle.ts
|
|
3056
|
-
import
|
|
3057
|
-
import
|
|
3156
|
+
import fs18 from "fs";
|
|
3157
|
+
import path25 from "path";
|
|
3058
3158
|
import { createHash } from "crypto";
|
|
3059
3159
|
|
|
3060
3160
|
// src/adapters/pi.ts
|
|
3061
|
-
import
|
|
3161
|
+
import path23 from "path";
|
|
3162
|
+
function piSystemPromptFile(targetDir) {
|
|
3163
|
+
const configDir = targetDir === void 0 ? process.env.PI_CODING_AGENT_DIR ?? path23.join(HOME, ".pi", "agent") : path23.join(path23.resolve(targetDir), "pi-agent");
|
|
3164
|
+
return path23.join(configDir, "AGENTS.md");
|
|
3165
|
+
}
|
|
3062
3166
|
var piAdapter = {
|
|
3063
3167
|
id: "pi",
|
|
3064
3168
|
paths(configDir) {
|
|
3065
|
-
const piConfigDir =
|
|
3066
|
-
const agentsHome = samePath(configDir, piConfigDir) ? HOME :
|
|
3169
|
+
const piConfigDir = path23.dirname(piSystemPromptFile());
|
|
3170
|
+
const agentsHome = samePath(configDir, piConfigDir) ? HOME : path23.join(path23.dirname(configDir), "home");
|
|
3067
3171
|
return {
|
|
3068
|
-
systemPromptFile:
|
|
3069
|
-
agentsDir:
|
|
3070
|
-
skillsDir:
|
|
3071
|
-
commandsDir:
|
|
3172
|
+
systemPromptFile: path23.join(configDir, "AGENTS.md"),
|
|
3173
|
+
agentsDir: path23.join(configDir, "agents"),
|
|
3174
|
+
skillsDir: path23.join(agentsHome, ".agents", "skills"),
|
|
3175
|
+
commandsDir: path23.join(configDir, "prompts"),
|
|
3072
3176
|
pluginsDir: null,
|
|
3073
|
-
scriptsDir:
|
|
3177
|
+
scriptsDir: path23.join(configDir, "scripts"),
|
|
3074
3178
|
outputStylesDir: null,
|
|
3075
3179
|
profilesDir: null
|
|
3076
3180
|
};
|
|
@@ -3080,11 +3184,20 @@ var piAdapter = {
|
|
|
3080
3184
|
},
|
|
3081
3185
|
injectEngramProtocol() {
|
|
3082
3186
|
return true;
|
|
3187
|
+
},
|
|
3188
|
+
// Pi 0.8.18 recompone estos marcadores desde su snapshot: no conoce los nuevos.
|
|
3189
|
+
adaptSystemPromptSections(sections) {
|
|
3190
|
+
const { context7, playwright, "chrome-devtools": devtools, ...legacy } = sections;
|
|
3191
|
+
return {
|
|
3192
|
+
...legacy,
|
|
3193
|
+
"system-prompt": [legacy["system-prompt"], context7].filter(Boolean).join("\n\n"),
|
|
3194
|
+
browser: [playwright, devtools].filter(Boolean).join("\n\n")
|
|
3195
|
+
};
|
|
3083
3196
|
}
|
|
3084
3197
|
};
|
|
3085
3198
|
|
|
3086
3199
|
// src/lib/pi-package-lifecycle.ts
|
|
3087
|
-
import
|
|
3200
|
+
import path24 from "path";
|
|
3088
3201
|
var REQUIRED_CAPABILITIES = /* @__PURE__ */ new Set([
|
|
3089
3202
|
"foundation-contract-v1",
|
|
3090
3203
|
"runner-json-v1",
|
|
@@ -3216,7 +3329,7 @@ function parseReceiptShape(receiptJson) {
|
|
|
3216
3329
|
return null;
|
|
3217
3330
|
}
|
|
3218
3331
|
if (engram === void 0) return "upgrade-required";
|
|
3219
|
-
if (engram === null || typeof engram !== "object" || Array.isArray(engram) || typeof Reflect.get(engram, "binary") !== "string" || !
|
|
3332
|
+
if (engram === null || typeof engram !== "object" || Array.isArray(engram) || typeof Reflect.get(engram, "binary") !== "string" || !path24.isAbsolute(Reflect.get(engram, "binary"))) {
|
|
3220
3333
|
return null;
|
|
3221
3334
|
}
|
|
3222
3335
|
return parsed;
|
|
@@ -3253,7 +3366,7 @@ function planPiPackageLifecycle(input) {
|
|
|
3253
3366
|
if (input.receiptJson !== null) {
|
|
3254
3367
|
const parsedReceipt = parseReceipt(input.receiptJson, input.candidate, {
|
|
3255
3368
|
kind: input.scope.kind,
|
|
3256
|
-
codingAgentDir:
|
|
3369
|
+
codingAgentDir: path24.resolve(input.scope.codingAgentDir)
|
|
3257
3370
|
}, input.engramBin);
|
|
3258
3371
|
if (parsedReceipt === "upgrade-required") return blocked(input, "receipt-upgrade-required");
|
|
3259
3372
|
if (parsedReceipt === null) return blocked(input, "receipt-corrupt");
|
|
@@ -3288,7 +3401,7 @@ function planPiPackageLifecycle(input) {
|
|
|
3288
3401
|
},
|
|
3289
3402
|
receipt: expectedReceipt(input.candidate, "installing", {
|
|
3290
3403
|
kind: input.scope.kind,
|
|
3291
|
-
codingAgentDir:
|
|
3404
|
+
codingAgentDir: path24.resolve(input.scope.codingAgentDir)
|
|
3292
3405
|
}, input.engramBin),
|
|
3293
3406
|
ownership: ownership(true)
|
|
3294
3407
|
};
|
|
@@ -3303,7 +3416,7 @@ function parseRunnerRecord(stdout, stderr, command, candidate, packageRunner) {
|
|
|
3303
3416
|
const parsed = JSON.parse(body);
|
|
3304
3417
|
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) return null;
|
|
3305
3418
|
const record = parsed;
|
|
3306
|
-
if (record.schemaVersion !== candidate.contract.runner.schemaVersion || record.command !== command || record.ok !== true || record.package === null || typeof record.package !== "object" || record.package.name !== candidate.package.name || record.package.version !== candidate.package.version || typeof record.package.root !== "string" || !
|
|
3419
|
+
if (record.schemaVersion !== candidate.contract.runner.schemaVersion || record.command !== command || record.ok !== true || record.package === null || typeof record.package !== "object" || record.package.name !== candidate.package.name || record.package.version !== candidate.package.version || typeof record.package.root !== "string" || !path24.isAbsolute(record.package.root) || path24.resolve(packageRunner) !== path24.resolve(record.package.root, "bin", "jorgex-pi.mjs")) {
|
|
3307
3420
|
return null;
|
|
3308
3421
|
}
|
|
3309
3422
|
return record;
|
|
@@ -3389,10 +3502,10 @@ function validateOwnedOperationState(input) {
|
|
|
3389
3502
|
}))) {
|
|
3390
3503
|
return { kind: "blocked", reason: "receipt-untrusted" };
|
|
3391
3504
|
}
|
|
3392
|
-
if (receipt.scope.kind !== (input.paths.targetDir ? "target-dir" : "real") ||
|
|
3505
|
+
if (receipt.scope.kind !== (input.paths.targetDir ? "target-dir" : "real") || path24.resolve(receipt.scope.codingAgentDir) !== path24.resolve(input.paths.codingAgentDir)) {
|
|
3393
3506
|
return { kind: "blocked", reason: "source-divergent" };
|
|
3394
3507
|
}
|
|
3395
|
-
if (input.engramBin !== null &&
|
|
3508
|
+
if (input.engramBin !== null && path24.resolve(receipt.engram.binary) !== path24.resolve(input.engramBin)) {
|
|
3396
3509
|
return { kind: "blocked", reason: "receipt-corrupt" };
|
|
3397
3510
|
}
|
|
3398
3511
|
const source = receipt.candidate.package.source;
|
|
@@ -3499,7 +3612,7 @@ var RETIRED_PLAYWRIGHT_FILES = [
|
|
|
3499
3612
|
"references/video-recording.md"
|
|
3500
3613
|
];
|
|
3501
3614
|
function retiredPlaywrightFiles(scope) {
|
|
3502
|
-
return RETIRED_PLAYWRIGHT_FILES.map((file) =>
|
|
3615
|
+
return RETIRED_PLAYWRIGHT_FILES.map((file) => path25.resolve(scope.home, ".agents", "skills", "playwright-cli", file));
|
|
3503
3616
|
}
|
|
3504
3617
|
function handoffEnabled(input, kind) {
|
|
3505
3618
|
return (kind === "devtools" ? input.devtoolsMcpEnabled : input.playwrightHandoffEnabled) === true;
|
|
@@ -3508,18 +3621,18 @@ var preparedPiProjectionUninstalls = /* @__PURE__ */ new WeakMap();
|
|
|
3508
3621
|
function projectionScope(scope) {
|
|
3509
3622
|
return {
|
|
3510
3623
|
...scope,
|
|
3511
|
-
settingsFile:
|
|
3624
|
+
settingsFile: path25.join(scope.codingAgentDir, "settings.json")
|
|
3512
3625
|
};
|
|
3513
3626
|
}
|
|
3514
3627
|
function isInside(root, file) {
|
|
3515
|
-
const relative =
|
|
3516
|
-
return relative === "" || relative !== ".." && !relative.startsWith(`..${
|
|
3628
|
+
const relative = path25.relative(path25.resolve(root), path25.resolve(file));
|
|
3629
|
+
return relative === "" || relative !== ".." && !relative.startsWith(`..${path25.sep}`) && !path25.isAbsolute(relative);
|
|
3517
3630
|
}
|
|
3518
3631
|
function isManagedPath(scope, file) {
|
|
3519
3632
|
return isInside(scope.home, file) || isInside(scope.codingAgentDir, file);
|
|
3520
3633
|
}
|
|
3521
3634
|
function isAllowedPath(scope, file) {
|
|
3522
|
-
return isManagedPath(scope, file) ||
|
|
3635
|
+
return isManagedPath(scope, file) || path25.resolve(file) === path25.resolve(scope.receiptFile);
|
|
3523
3636
|
}
|
|
3524
3637
|
function projectedPiAdapter(scope) {
|
|
3525
3638
|
const paths = piAdapter.paths(scope.codingAgentDir);
|
|
@@ -3527,11 +3640,11 @@ function projectedPiAdapter(scope) {
|
|
|
3527
3640
|
...piAdapter,
|
|
3528
3641
|
paths: () => ({
|
|
3529
3642
|
...paths,
|
|
3530
|
-
skillsDir:
|
|
3643
|
+
skillsDir: path25.join(scope.home, ".agents", "skills")
|
|
3531
3644
|
})
|
|
3532
3645
|
};
|
|
3533
3646
|
}
|
|
3534
|
-
function projectionPlan(input, scope) {
|
|
3647
|
+
function projectionPlan(input, scope, includePrompt = true) {
|
|
3535
3648
|
const ctx = {
|
|
3536
3649
|
writingStyle: input.writingStyle,
|
|
3537
3650
|
stackDir: input.stackDir,
|
|
@@ -3544,7 +3657,7 @@ function projectionPlan(input, scope) {
|
|
|
3544
3657
|
};
|
|
3545
3658
|
const adapter = projectedPiAdapter(scope);
|
|
3546
3659
|
const actions = [
|
|
3547
|
-
...planSystemPrompt(adapter, ctx),
|
|
3660
|
+
...includePrompt ? planSystemPrompt(adapter, ctx) : [],
|
|
3548
3661
|
...planSkills(adapter, ctx),
|
|
3549
3662
|
...planCommands(adapter, ctx)
|
|
3550
3663
|
];
|
|
@@ -3571,20 +3684,20 @@ function projectionPlan(input, scope) {
|
|
|
3571
3684
|
return actions;
|
|
3572
3685
|
}
|
|
3573
3686
|
function handoffPath(scope, kind) {
|
|
3574
|
-
return
|
|
3687
|
+
return path25.resolve(scope.codingAgentDir, "jorgex-pi", `${kind}.v1.json`);
|
|
3575
3688
|
}
|
|
3576
3689
|
function contentHash(content) {
|
|
3577
3690
|
return createHash("sha256").update(content).digest("hex");
|
|
3578
3691
|
}
|
|
3579
3692
|
function canonicalActionContent(action) {
|
|
3580
|
-
return action.kind === "write" ? action.content :
|
|
3693
|
+
return action.kind === "write" ? action.content : fs18.readFileSync(action.source, "utf8");
|
|
3581
3694
|
}
|
|
3582
3695
|
function hasActionDrift(action, deps) {
|
|
3583
3696
|
return deps.readText(action.target) !== canonicalActionContent(action);
|
|
3584
3697
|
}
|
|
3585
3698
|
function applyActions(actions, deps, handoffs = []) {
|
|
3586
3699
|
for (const action of actions) {
|
|
3587
|
-
const handoff = handoffs.find((entry) =>
|
|
3700
|
+
const handoff = handoffs.find((entry) => path25.resolve(action.target) === entry.file);
|
|
3588
3701
|
if (handoff) {
|
|
3589
3702
|
try {
|
|
3590
3703
|
if (deps.readText(handoff.file) !== handoff.previous) return handoffConflict(handoff.kind, handoff.file);
|
|
@@ -3604,9 +3717,9 @@ function assertPlanContained(plan, scope) {
|
|
|
3604
3717
|
}
|
|
3605
3718
|
}
|
|
3606
3719
|
function receiptFor(plan, scope) {
|
|
3607
|
-
const systemPrompt =
|
|
3720
|
+
const systemPrompt = path25.join(scope.codingAgentDir, "AGENTS.md");
|
|
3608
3721
|
const owned = [...new Set(
|
|
3609
|
-
plan.map((action) =>
|
|
3722
|
+
plan.map((action) => path25.resolve(action.target)).filter((target) => target !== systemPrompt)
|
|
3610
3723
|
)];
|
|
3611
3724
|
const receipt = {
|
|
3612
3725
|
schemaVersion: 1,
|
|
@@ -3619,7 +3732,7 @@ function receiptFor(plan, scope) {
|
|
|
3619
3732
|
owned
|
|
3620
3733
|
};
|
|
3621
3734
|
for (const kind of HANDOFF_KINDS) {
|
|
3622
|
-
const action = plan.find((entry) =>
|
|
3735
|
+
const action = plan.find((entry) => path25.resolve(entry.target) === handoffPath(scope, kind));
|
|
3623
3736
|
if (action) receipt[kind] = { sha256: contentHash(canonicalActionContent(action)) };
|
|
3624
3737
|
}
|
|
3625
3738
|
return receipt;
|
|
@@ -3660,7 +3773,7 @@ function parseReceipt2(raw, expected) {
|
|
|
3660
3773
|
if (matches(expectedOwned)) return { schemaVersion: 1, scope: expected.scope, owned: expectedOwned, ...digests };
|
|
3661
3774
|
const retired = retiredPlaywrightFiles(expected.scope);
|
|
3662
3775
|
if (retired.some((file) => expectedOwned.includes(file))) return null;
|
|
3663
|
-
const skillsRoot = `${
|
|
3776
|
+
const skillsRoot = `${path25.resolve(expected.scope.home, ".agents", "skills")}${path25.sep}`;
|
|
3664
3777
|
const insertion = expectedOwned.findIndex((file) => !file.startsWith(skillsRoot) || file > retired[0]);
|
|
3665
3778
|
const legacyOwned = [...expectedOwned];
|
|
3666
3779
|
legacyOwned.splice(insertion === -1 ? legacyOwned.length : insertion, 0, ...retired);
|
|
@@ -3671,7 +3784,7 @@ function parseReceipt2(raw, expected) {
|
|
|
3671
3784
|
}
|
|
3672
3785
|
}
|
|
3673
3786
|
function expectedProjectionReceipt(input, scope) {
|
|
3674
|
-
const plan = projectionPlan(input, scope);
|
|
3787
|
+
const plan = projectionPlan(input, scope, false);
|
|
3675
3788
|
assertPlanContained(plan, scope);
|
|
3676
3789
|
return receiptFor(plan, scope);
|
|
3677
3790
|
}
|
|
@@ -3679,13 +3792,13 @@ function realPiProjectionScope() {
|
|
|
3679
3792
|
return {
|
|
3680
3793
|
kind: "real",
|
|
3681
3794
|
home: HOME,
|
|
3682
|
-
codingAgentDir:
|
|
3683
|
-
receiptFile:
|
|
3795
|
+
codingAgentDir: path25.dirname(piSystemPromptFile()),
|
|
3796
|
+
receiptFile: path25.join(dataDir(), "pi-projection-receipt.json")
|
|
3684
3797
|
};
|
|
3685
3798
|
}
|
|
3686
3799
|
function readTextOnlyIfMissing(file) {
|
|
3687
3800
|
try {
|
|
3688
|
-
return
|
|
3801
|
+
return fs18.readFileSync(file, "utf8");
|
|
3689
3802
|
} catch (error) {
|
|
3690
3803
|
if (typeof error === "object" && error !== null && Reflect.get(error, "code") === "ENOENT") return null;
|
|
3691
3804
|
throw error;
|
|
@@ -3718,13 +3831,13 @@ function manifestOwned(manifest) {
|
|
|
3718
3831
|
return new Set([
|
|
3719
3832
|
...manifest.runtimes.codex?.owned ?? [],
|
|
3720
3833
|
...manifest.runtimes.opencode?.owned ?? []
|
|
3721
|
-
].map((file) =>
|
|
3834
|
+
].map((file) => path25.resolve(file)));
|
|
3722
3835
|
}
|
|
3723
3836
|
function withoutManagedPromptSections(content) {
|
|
3724
|
-
return
|
|
3837
|
+
return removeSystemPromptSections(content);
|
|
3725
3838
|
}
|
|
3726
3839
|
function uniquePaths(paths) {
|
|
3727
|
-
return [...new Set(paths.map((file) =>
|
|
3840
|
+
return [...new Set(paths.map((file) => path25.resolve(file)))];
|
|
3728
3841
|
}
|
|
3729
3842
|
function blocked2(reason, paths, remedy) {
|
|
3730
3843
|
return { kind: "blocked", reason, paths: uniquePaths(paths), remedy };
|
|
@@ -3831,15 +3944,20 @@ function preparePiProjectionUninstall(input, deps) {
|
|
|
3831
3944
|
}
|
|
3832
3945
|
const scope = projectionScope(input.scope);
|
|
3833
3946
|
const expectedReceipt2 = expectedProjectionReceipt(input, scope);
|
|
3834
|
-
const prompt =
|
|
3947
|
+
const prompt = path25.join(scope.codingAgentDir, "AGENTS.md");
|
|
3835
3948
|
let existingPrompt;
|
|
3836
3949
|
try {
|
|
3837
3950
|
existingPrompt = deps.readText(prompt);
|
|
3838
3951
|
} catch {
|
|
3839
3952
|
return cleanupFailure([prompt]);
|
|
3840
3953
|
}
|
|
3954
|
+
try {
|
|
3955
|
+
assertSystemPromptMarkers(existingPrompt, prompt);
|
|
3956
|
+
} catch (error) {
|
|
3957
|
+
return blocked2("projection-prompt-markers", [prompt], error instanceof Error ? error.message : String(error));
|
|
3958
|
+
}
|
|
3841
3959
|
const promptContent = existingPrompt === null ? null : withoutManagedPromptSections(existingPrompt);
|
|
3842
|
-
const promptUpdate = promptContent === null || promptContent === existingPrompt ? null : { file:
|
|
3960
|
+
const promptUpdate = promptContent === null || promptContent === existingPrompt ? null : { file: path25.resolve(prompt), content: promptContent };
|
|
3843
3961
|
const receiptRead = readProjectionReceipt(scope.receiptFile, deps);
|
|
3844
3962
|
if (receiptRead.kind === "unreadable") return receiptUnreadable(scope.receiptFile);
|
|
3845
3963
|
let ownedToRemove = [];
|
|
@@ -3857,7 +3975,7 @@ function preparePiProjectionUninstall(input, deps) {
|
|
|
3857
3975
|
return cleanupFailure([scope.receiptFile]);
|
|
3858
3976
|
}
|
|
3859
3977
|
ownedToRemove = receipt.owned.filter((owned) => !retained.has(owned));
|
|
3860
|
-
receiptFile =
|
|
3978
|
+
receiptFile = path25.resolve(scope.receiptFile);
|
|
3861
3979
|
backupTargets = [...backupTargets, ...receipt.owned, receiptFile];
|
|
3862
3980
|
}
|
|
3863
3981
|
const handoffs = [];
|
|
@@ -3935,10 +4053,16 @@ function runPiProjectionLifecycle(input, deps) {
|
|
|
3935
4053
|
const prepared = preparePiProjectionUninstall(input, deps);
|
|
3936
4054
|
return prepared.kind === "blocked" ? prepared : completePiProjectionUninstall(prepared.plan, deps);
|
|
3937
4055
|
}
|
|
3938
|
-
|
|
4056
|
+
const prompt = path25.join(scope.codingAgentDir, "AGENTS.md");
|
|
4057
|
+
try {
|
|
4058
|
+
assertSystemPromptMarkers(deps.readText(prompt), prompt);
|
|
4059
|
+
} catch (error) {
|
|
4060
|
+
return blocked2("projection-prompt-markers", [prompt], error instanceof Error ? error.message : String(error));
|
|
4061
|
+
}
|
|
4062
|
+
if (input.devtoolsMcpEnabled && (!input.pnpmBin || !path25.isAbsolute(input.pnpmBin))) {
|
|
3939
4063
|
return blocked2("projection-devtools-command", [handoffPath(scope, "devtools")], "DevTools requiere un ejecutable pnpm absoluto disponible en PATH.");
|
|
3940
4064
|
}
|
|
3941
|
-
if (input.playwrightHandoffEnabled && (!input.playwrightCliCommand || !
|
|
4065
|
+
if (input.playwrightHandoffEnabled && (!input.playwrightCliCommand || !path25.isAbsolute(input.playwrightCliCommand) || /[\u0000-\u001f\u007f]/.test(input.playwrightCliCommand))) {
|
|
3942
4066
|
return blocked2("projection-playwright-command", [handoffPath(scope, "playwright")], "Playwright requiere un ejecutable absoluto verificado. Abre una terminal nueva tras pnpm setup y reintenta install --playwright.");
|
|
3943
4067
|
}
|
|
3944
4068
|
const plan = projectionPlan(input, scope);
|
|
@@ -3951,12 +4075,12 @@ function runPiProjectionLifecycle(input, deps) {
|
|
|
3951
4075
|
const expectedReceipt2 = receiptContent(receipt);
|
|
3952
4076
|
const drifted = plan.filter((action) => hasActionDrift(action, deps));
|
|
3953
4077
|
for (const { kind, file } of checked.handoffs) {
|
|
3954
|
-
if (handoffEnabled(input, kind) && checked.previous?.[kind]?.sha256 !== receipt[kind]?.sha256 && !drifted.some((action) =>
|
|
4078
|
+
if (handoffEnabled(input, kind) && checked.previous?.[kind]?.sha256 !== receipt[kind]?.sha256 && !drifted.some((action) => path25.resolve(action.target) === file)) {
|
|
3955
4079
|
return input.operation === "doctor" ? { kind: "drift", paths: [file] } : handoffConflict(kind, file);
|
|
3956
4080
|
}
|
|
3957
4081
|
}
|
|
3958
4082
|
if (input.operation === "doctor") {
|
|
3959
|
-
const paths = drifted.map((action) =>
|
|
4083
|
+
const paths = drifted.map((action) => path25.resolve(action.target));
|
|
3960
4084
|
paths.push(...removedHandoffs.map(({ file }) => file), ...retiredSkills);
|
|
3961
4085
|
const currentSettings2 = deps.readText(scope.settingsFile);
|
|
3962
4086
|
if (currentSettings2 === null || filterProjectedPiPackage(currentSettings2, input.packageSource) !== currentSettings2) {
|
|
@@ -4015,12 +4139,12 @@ function runPiProjectionLifecycle(input, deps) {
|
|
|
4015
4139
|
return { kind: "synced", changed: drifted.length > 0 || packageWillChange || receiptChanged || removedHandoffs.length > 0 };
|
|
4016
4140
|
}
|
|
4017
4141
|
function systemProjectionLifecycle(input) {
|
|
4018
|
-
const targetRoot = input.targetDir === void 0 ? null :
|
|
4142
|
+
const targetRoot = input.targetDir === void 0 ? null : path25.resolve(input.targetDir);
|
|
4019
4143
|
const scope = targetRoot === null ? realPiProjectionScope() : {
|
|
4020
4144
|
kind: "target-dir",
|
|
4021
|
-
home:
|
|
4022
|
-
codingAgentDir:
|
|
4023
|
-
receiptFile:
|
|
4145
|
+
home: path25.join(targetRoot, "home"),
|
|
4146
|
+
codingAgentDir: path25.dirname(piSystemPromptFile(targetRoot)),
|
|
4147
|
+
receiptFile: path25.join(targetRoot, "state", "pi-projection-receipt.json")
|
|
4024
4148
|
};
|
|
4025
4149
|
return {
|
|
4026
4150
|
input: {
|
|
@@ -4041,14 +4165,14 @@ function systemProjectionLifecycle(input) {
|
|
|
4041
4165
|
backup: (paths) => createBackup(
|
|
4042
4166
|
paths,
|
|
4043
4167
|
`pi-projection-${input.operation}`,
|
|
4044
|
-
targetRoot === null ? void 0 :
|
|
4168
|
+
targetRoot === null ? void 0 : path25.join(targetRoot, "backups")
|
|
4045
4169
|
),
|
|
4046
4170
|
writeText,
|
|
4047
4171
|
copyFile,
|
|
4048
4172
|
removeFile: (file) => {
|
|
4049
|
-
|
|
4050
|
-
if (retiredPlaywrightFiles(scope).includes(
|
|
4051
|
-
pruneEmptyDirs(file,
|
|
4173
|
+
fs18.rmSync(file, { force: true });
|
|
4174
|
+
if (retiredPlaywrightFiles(scope).includes(path25.resolve(file))) {
|
|
4175
|
+
pruneEmptyDirs(file, path25.join(scope.home, ".agents", "skills"));
|
|
4052
4176
|
}
|
|
4053
4177
|
},
|
|
4054
4178
|
readManifest: targetRoot === null ? readManifest : () => ({ runtimes: {} })
|
|
@@ -4056,6 +4180,8 @@ function systemProjectionLifecycle(input) {
|
|
|
4056
4180
|
};
|
|
4057
4181
|
}
|
|
4058
4182
|
function preparePiProjectionUninstallSystem(input) {
|
|
4183
|
+
const error = systemPromptPreflight(input.targetDir);
|
|
4184
|
+
if (error) return error;
|
|
4059
4185
|
const lifecycle = systemProjectionLifecycle(input);
|
|
4060
4186
|
return preparePiProjectionUninstall(lifecycle.input, lifecycle.deps);
|
|
4061
4187
|
}
|
|
@@ -4064,9 +4190,19 @@ function completePiProjectionUninstallSystem(plan, input) {
|
|
|
4064
4190
|
return completePiProjectionUninstall(plan, lifecycle.deps);
|
|
4065
4191
|
}
|
|
4066
4192
|
function runPiProjectionLifecycleSystem(input) {
|
|
4193
|
+
const error = systemPromptPreflight(input.targetDir);
|
|
4194
|
+
if (error) return error;
|
|
4067
4195
|
const lifecycle = systemProjectionLifecycle(input);
|
|
4068
4196
|
return runPiProjectionLifecycle(lifecycle.input, lifecycle.deps);
|
|
4069
4197
|
}
|
|
4198
|
+
function systemPromptPreflight(targetDir) {
|
|
4199
|
+
const target = piSystemPromptFile(targetDir);
|
|
4200
|
+
try {
|
|
4201
|
+
assertSystemPromptFile(target, targetDir);
|
|
4202
|
+
} catch (error) {
|
|
4203
|
+
return blocked2("projection-prompt-markers", [target], error instanceof Error ? error.message : String(error));
|
|
4204
|
+
}
|
|
4205
|
+
}
|
|
4070
4206
|
|
|
4071
4207
|
// src/uninstall.ts
|
|
4072
4208
|
function resolvePlaywrightUninstallPlan(input) {
|
|
@@ -4077,6 +4213,18 @@ function resolvePlaywrightUninstallPlan(input) {
|
|
|
4077
4213
|
}
|
|
4078
4214
|
async function runUninstall(opts) {
|
|
4079
4215
|
p2.intro(`jorgex-stack ${opts.dryRun ? "uninstall (dry-run)" : "uninstall"}`);
|
|
4216
|
+
try {
|
|
4217
|
+
for (const id of opts.runtimes) {
|
|
4218
|
+
const adapter = ADAPTERS[id];
|
|
4219
|
+
if (!adapter) continue;
|
|
4220
|
+
const detection = adapter.detect();
|
|
4221
|
+
if (opts.targetDir === void 0 && !detection.installed) continue;
|
|
4222
|
+
assertSystemPromptFile(adapter.paths(opts.targetDir ?? detection.configDir).systemPromptFile, opts.targetDir);
|
|
4223
|
+
}
|
|
4224
|
+
} catch (error) {
|
|
4225
|
+
p2.log.error(error instanceof Error ? error.message : String(error));
|
|
4226
|
+
return 1;
|
|
4227
|
+
}
|
|
4080
4228
|
const useBrowserPreferences = opts.targetDir === void 0;
|
|
4081
4229
|
const preferenceErrors = useBrowserPreferences ? [...browserPreferenceErrors(), primaryModelOwnershipError()].filter((error) => error !== null) : [];
|
|
4082
4230
|
if (preferenceErrors.length > 0) {
|
|
@@ -4127,7 +4275,7 @@ async function runUninstall(opts) {
|
|
|
4127
4275
|
if (!detection.installed) continue;
|
|
4128
4276
|
const keepCtx = makeContext(keep, detection.configDir);
|
|
4129
4277
|
if (!keepCtx) continue;
|
|
4130
|
-
for (const action of buildPlan(keep, keepCtx)) retained.add(
|
|
4278
|
+
for (const action of buildPlan(keep, keepCtx)) retained.add(path26.resolve(action.target));
|
|
4131
4279
|
}
|
|
4132
4280
|
}
|
|
4133
4281
|
for (const id of opts.runtimes) {
|
|
@@ -4146,15 +4294,15 @@ async function runUninstall(opts) {
|
|
|
4146
4294
|
if (!ctx) continue;
|
|
4147
4295
|
ctx.preserveEngram = !removeEngram;
|
|
4148
4296
|
const unmerge = adapter.planUnmerge(mcpForUnmerge, hooks, ctx);
|
|
4149
|
-
const mergedTargets = new Set(unmerge.map((a) =>
|
|
4297
|
+
const mergedTargets = new Set(unmerge.map((a) => path26.resolve(a.target)));
|
|
4150
4298
|
const usingRealConfig = opts.targetDir === void 0;
|
|
4151
4299
|
const prevOwned = usingRealConfig ? readManifest().runtimes[id]?.owned ?? [] : [];
|
|
4152
|
-
const pruneRoot = usingRealConfig ? HOME :
|
|
4300
|
+
const pruneRoot = usingRealConfig ? HOME : path26.dirname(configDir);
|
|
4153
4301
|
const planTargets = [
|
|
4154
|
-
.../* @__PURE__ */ new Set([...buildPlan(adapter, ctx).map((a) =>
|
|
4155
|
-
].filter((t) => !mergedTargets.has(t) &&
|
|
4302
|
+
.../* @__PURE__ */ new Set([...buildPlan(adapter, ctx).map((a) => path26.resolve(a.target)), ...prevOwned.map((t) => path26.resolve(t))])
|
|
4303
|
+
].filter((t) => !mergedTargets.has(t) && fs19.existsSync(t));
|
|
4156
4304
|
const deleteTargets = planTargets.filter(
|
|
4157
|
-
(t) => !retained.has(t) && !(ctx.preserveEngram &&
|
|
4305
|
+
(t) => !retained.has(t) && !(ctx.preserveEngram && path26.basename(t) === "engram.ts") && isContainedIn(t, pruneRoot)
|
|
4158
4306
|
);
|
|
4159
4307
|
const sharedKept = planTargets.length - deleteTargets.length;
|
|
4160
4308
|
p2.log.step(`${adapter.name} \u2192 ${configDir}`);
|
|
@@ -4164,7 +4312,7 @@ async function runUninstall(opts) {
|
|
|
4164
4312
|
let backup;
|
|
4165
4313
|
try {
|
|
4166
4314
|
backup = createBackup(
|
|
4167
|
-
[...deleteTargets, ...unmerge.map((a) => a.target).filter((t) =>
|
|
4315
|
+
[...deleteTargets, ...unmerge.map((a) => a.target).filter((t) => fs19.existsSync(t))],
|
|
4168
4316
|
`uninstall-${id}`
|
|
4169
4317
|
);
|
|
4170
4318
|
} catch (error) {
|
|
@@ -4174,13 +4322,13 @@ async function runUninstall(opts) {
|
|
|
4174
4322
|
}
|
|
4175
4323
|
if (backup) p2.log.info(`Backup: ${backup.id} (${backup.files.length} archivos)`);
|
|
4176
4324
|
for (const target of deleteTargets) {
|
|
4177
|
-
|
|
4325
|
+
fs19.rmSync(target, { force: true });
|
|
4178
4326
|
pruneEmptyDirs(target, pruneRoot);
|
|
4179
4327
|
}
|
|
4180
4328
|
for (const action of unmerge) {
|
|
4181
4329
|
if (action.kind !== "write") continue;
|
|
4182
4330
|
if (action.content.trim() === "") {
|
|
4183
|
-
|
|
4331
|
+
fs19.rmSync(action.target, { force: true });
|
|
4184
4332
|
} else {
|
|
4185
4333
|
writeText(action.target, action.content);
|
|
4186
4334
|
}
|
|
@@ -4234,12 +4382,12 @@ async function runUninstall(opts) {
|
|
|
4234
4382
|
}
|
|
4235
4383
|
|
|
4236
4384
|
// src/doctor.ts
|
|
4237
|
-
import
|
|
4238
|
-
import
|
|
4385
|
+
import path27 from "path";
|
|
4386
|
+
import fs20 from "fs";
|
|
4239
4387
|
import * as p3 from "@clack/prompts";
|
|
4240
4388
|
function readDoctorTextIfExists(file) {
|
|
4241
4389
|
try {
|
|
4242
|
-
return
|
|
4390
|
+
return fs20.readFileSync(file, "utf8");
|
|
4243
4391
|
} catch (error) {
|
|
4244
4392
|
if (error.code === "ENOENT") return null;
|
|
4245
4393
|
throw error;
|
|
@@ -4259,11 +4407,12 @@ function resolvePlaywrightDoctorState(input) {
|
|
|
4259
4407
|
if (input.browserCache?.status === "unreadable") {
|
|
4260
4408
|
return { status: "unreadable", path: input.browserCache.path, errorCode: input.browserCache.errorCode };
|
|
4261
4409
|
}
|
|
4410
|
+
if (input.browserVerified === false && input.browserCache?.status === "ready") return { status: "broken" };
|
|
4262
4411
|
if (!input.browserReady) return { status: "missing", missing: "browser" };
|
|
4263
4412
|
return { status: "healthy" };
|
|
4264
4413
|
}
|
|
4265
4414
|
function context7KeyConfigured(id, configDir) {
|
|
4266
|
-
const file = id === "codex" ?
|
|
4415
|
+
const file = id === "codex" ? path27.join(configDir, "config.toml") : id === "claude-code" ? path27.join(path27.dirname(configDir), `${path27.basename(configDir)}.json`) : path27.join(configDir, "opencode.json");
|
|
4267
4416
|
const content = readTextIfExists(file);
|
|
4268
4417
|
if (content === null) return null;
|
|
4269
4418
|
const match = /CONTEXT7_API_KEY"?\s*[:=]\s*"([^"]*)"/.exec(content);
|
|
@@ -4283,7 +4432,7 @@ function reportWritingStyle(options, style, mode) {
|
|
|
4283
4432
|
for (const id of runtimes) {
|
|
4284
4433
|
const adapter = id === "pi" ? piAdapter : ADAPTERS[id];
|
|
4285
4434
|
if (adapter === void 0) continue;
|
|
4286
|
-
const configDir = id === "pi" ? options.targetDir === void 0 ? process.env.PI_CODING_AGENT_DIR ??
|
|
4435
|
+
const configDir = id === "pi" ? options.targetDir === void 0 ? process.env.PI_CODING_AGENT_DIR ?? path27.join(HOME, ".pi", "agent") : path27.join(options.targetDir, "pi-agent") : options.targetDir ?? ADAPTERS[id].detect().configDir;
|
|
4287
4436
|
const file = adapter.paths(configDir).systemPromptFile;
|
|
4288
4437
|
try {
|
|
4289
4438
|
const content = readDoctorTextIfExists(file) ?? "";
|
|
@@ -4298,7 +4447,7 @@ function reportWritingStyle(options, style, mode) {
|
|
|
4298
4447
|
problems++;
|
|
4299
4448
|
}
|
|
4300
4449
|
if (id === "codex") {
|
|
4301
|
-
const override =
|
|
4450
|
+
const override = path27.join(configDir, "AGENTS.override.md");
|
|
4302
4451
|
if ((readDoctorTextIfExists(override) ?? "").trim() !== "") {
|
|
4303
4452
|
p3.log.warn(`Codex: ${override} no vac\xEDo puede ocultar el AGENTS.md gestionado; rev\xEDsalo sin modificarlo autom\xE1ticamente.`);
|
|
4304
4453
|
problems++;
|
|
@@ -4341,34 +4490,41 @@ async function runDoctor(options = {}) {
|
|
|
4341
4490
|
p3.log.success(`Engram: ${version} (${engramBin})`);
|
|
4342
4491
|
}
|
|
4343
4492
|
}
|
|
4344
|
-
const engramDataDir = process.env.ENGRAM_DATA_DIR ??
|
|
4345
|
-
const engramDb =
|
|
4346
|
-
if (
|
|
4347
|
-
const sizeMb = (
|
|
4493
|
+
const engramDataDir = process.env.ENGRAM_DATA_DIR ?? path27.join(HOME, ".engram");
|
|
4494
|
+
const engramDb = path27.join(engramDataDir, "engram.db");
|
|
4495
|
+
if (fs20.existsSync(engramDb)) {
|
|
4496
|
+
const sizeMb = (fs20.statSync(engramDb).size / 1024 / 1024).toFixed(1);
|
|
4348
4497
|
p3.log.info(`Engram DB: ${engramDb} (${sizeMb} MB de memorias \u2014 el stack no la toca JAM\xC1S).`);
|
|
4349
4498
|
}
|
|
4350
|
-
if (!
|
|
4499
|
+
if (!fs20.existsSync(modelMapFile())) p3.log.info("model-map: a\xFAn no creado (se crea en el primer install o con 'models').");
|
|
4351
4500
|
const preferenceErrors = browserPreferenceErrors();
|
|
4352
4501
|
const primaryOwnershipError = primaryModelOwnershipError();
|
|
4353
4502
|
if (primaryOwnershipError !== null) {
|
|
4354
4503
|
p3.log.error(primaryOwnershipError);
|
|
4355
4504
|
problems++;
|
|
4356
4505
|
}
|
|
4506
|
+
let effectivePlaywright;
|
|
4357
4507
|
if (preferenceErrors.length > 0) {
|
|
4358
4508
|
for (const error of preferenceErrors) p3.log.error(error);
|
|
4359
4509
|
problems += preferenceErrors.length;
|
|
4510
|
+
} else if (options.dryRun) {
|
|
4511
|
+
p3.log.info("Playwright CLI: comprobaci\xF3n omitida en dry-run; no se eval\xFAa el estado del paquete ni del navegador.");
|
|
4360
4512
|
} else {
|
|
4361
|
-
const
|
|
4513
|
+
const enabled = loadPlaywrightCliPreference();
|
|
4514
|
+
const capability = options.playwrightCapability ?? (enabled === true ? inspectPlaywrightCapability() : void 0);
|
|
4515
|
+
effectivePlaywright = capability?.effective;
|
|
4516
|
+
const cli = capability?.cli ?? { status: "absent" };
|
|
4362
4517
|
const playwright = resolvePlaywrightDoctorState({
|
|
4363
|
-
enabled
|
|
4364
|
-
cli
|
|
4365
|
-
browserReady:
|
|
4366
|
-
|
|
4518
|
+
enabled,
|
|
4519
|
+
cli,
|
|
4520
|
+
browserReady: capability?.effective ?? false,
|
|
4521
|
+
browserVerified: capability?.browserVerified,
|
|
4522
|
+
browserCache: capability?.browserCache
|
|
4367
4523
|
});
|
|
4368
4524
|
if (playwright.status === "disabled") {
|
|
4369
4525
|
p3.log.info("Playwright CLI: deshabilitado (opcional). Usa 'install --playwright' para instalarlo de forma expl\xEDcita.");
|
|
4370
4526
|
} else if (playwright.status === "healthy") {
|
|
4371
|
-
p3.log.success("Playwright CLI: paquete y
|
|
4527
|
+
p3.log.success("Playwright CLI: paquete y arranque de Chromium verificados.");
|
|
4372
4528
|
} else if (playwright.status === "not-in-path") {
|
|
4373
4529
|
p3.log.warn("Playwright CLI: instalado en el directorio de pnpm, pero fuera del PATH de esta terminal. Abre una terminal nueva tras pnpm setup; no hace falta reinstalarlo.");
|
|
4374
4530
|
problems++;
|
|
@@ -4377,7 +4533,8 @@ async function runDoctor(options = {}) {
|
|
|
4377
4533
|
p3.log.warn(`Playwright CLI: habilitado, pero falta ${target} \u2192 ejecuta 'jorgex-stack install --playwright'.`);
|
|
4378
4534
|
problems++;
|
|
4379
4535
|
} else if (playwright.status === "broken") {
|
|
4380
|
-
|
|
4536
|
+
const launchFailed = capability?.cli.status === "current" && capability.browserCache.status === "ready" && capability.browserVerified === false;
|
|
4537
|
+
p3.log.error(`Playwright CLI: ${launchFailed ? "Chromium no arranca" : "el binario detectado no responde correctamente"} \u2192 ejecuta 'jorgex-stack install --playwright'.`);
|
|
4381
4538
|
problems++;
|
|
4382
4539
|
} else if (playwright.status === "unreadable") {
|
|
4383
4540
|
p3.log.error(`Playwright CLI: no se puede leer la cach\xE9 de navegadores en ${playwright.path} (${playwright.errorCode}) \u2192 revisa permisos o ejecuta 'jorgex-stack install --playwright'.`);
|
|
@@ -4388,7 +4545,7 @@ async function runDoctor(options = {}) {
|
|
|
4388
4545
|
}
|
|
4389
4546
|
}
|
|
4390
4547
|
const manifest = readManifest();
|
|
4391
|
-
const current = collectAllCurrentTargets(modePreference);
|
|
4548
|
+
const current = collectAllCurrentTargets(modePreference, effectivePlaywright);
|
|
4392
4549
|
if (!current.complete || current.warnings.length > 0) {
|
|
4393
4550
|
p3.log.warn("Limpieza de hu\xE9rfanos deshabilitada: no se pudo construir el plan completo de todos los runtimes.");
|
|
4394
4551
|
for (const warning of current.warnings) p3.log.warn(warning);
|
|
@@ -4404,7 +4561,7 @@ async function runDoctor(options = {}) {
|
|
|
4404
4561
|
const capabilityReport = adapter.reportCapabilities(detection.configDir);
|
|
4405
4562
|
const capabilitySummary = capabilityReport.capabilities.map((capability) => `${capability.id}=${capability.state}`).join(", ");
|
|
4406
4563
|
p3.log.info(`${adapter.name}: capabilities diagnostic (${capabilitySummary}); no certifica enforcement local.`);
|
|
4407
|
-
const ctx = makeContext(adapter, detection.configDir, modePreference);
|
|
4564
|
+
const ctx = makeContext(adapter, detection.configDir, modePreference, true, effectivePlaywright);
|
|
4408
4565
|
if (!ctx) continue;
|
|
4409
4566
|
ctx.writingStyle = writingStyle;
|
|
4410
4567
|
let pending;
|
|
@@ -4429,7 +4586,7 @@ async function runDoctor(options = {}) {
|
|
|
4429
4586
|
p3.log.warn(`${adapter.name}: ${orphans.length} archivos hu\xE9rfanos de versiones previas \u2192 ejecuta 'sync'.`);
|
|
4430
4587
|
problems++;
|
|
4431
4588
|
}
|
|
4432
|
-
if (adapter.id === "codex" &&
|
|
4589
|
+
if (adapter.id === "codex" && fs20.existsSync(path27.join(detection.configDir, "hooks.json"))) {
|
|
4433
4590
|
p3.log.info("Codex: recuerda que los hooks requieren aprobaci\xF3n manual \u2014 verifica con /hooks dentro de codex.");
|
|
4434
4591
|
}
|
|
4435
4592
|
const key = context7KeyConfigured(adapter.id, detection.configDir);
|
|
@@ -4440,15 +4597,15 @@ async function runDoctor(options = {}) {
|
|
|
4440
4597
|
}
|
|
4441
4598
|
|
|
4442
4599
|
// src/update.ts
|
|
4443
|
-
import
|
|
4444
|
-
import
|
|
4600
|
+
import fs23 from "fs";
|
|
4601
|
+
import path30 from "path";
|
|
4445
4602
|
import os3 from "os";
|
|
4446
4603
|
import { execFileSync as execFileSync5 } from "child_process";
|
|
4447
4604
|
import * as p4 from "@clack/prompts";
|
|
4448
4605
|
|
|
4449
4606
|
// src/lib/github.ts
|
|
4450
|
-
import
|
|
4451
|
-
import
|
|
4607
|
+
import fs21 from "fs";
|
|
4608
|
+
import path28 from "path";
|
|
4452
4609
|
import { execFileSync as execFileSync3 } from "child_process";
|
|
4453
4610
|
import os2 from "os";
|
|
4454
4611
|
import { Readable } from "stream";
|
|
@@ -4518,19 +4675,19 @@ async function latestGithubCommit(repo) {
|
|
|
4518
4675
|
}
|
|
4519
4676
|
}
|
|
4520
4677
|
function validateExtractedTree(destDir) {
|
|
4521
|
-
const resolved =
|
|
4678
|
+
const resolved = path28.resolve(destDir);
|
|
4522
4679
|
const walk = (dir) => {
|
|
4523
4680
|
let entries;
|
|
4524
4681
|
try {
|
|
4525
|
-
entries =
|
|
4682
|
+
entries = fs21.readdirSync(dir, { withFileTypes: true });
|
|
4526
4683
|
} catch {
|
|
4527
4684
|
return false;
|
|
4528
4685
|
}
|
|
4529
4686
|
for (const entry of entries) {
|
|
4530
|
-
const full =
|
|
4687
|
+
const full = path28.join(dir, entry.name);
|
|
4531
4688
|
let stat;
|
|
4532
4689
|
try {
|
|
4533
|
-
stat =
|
|
4690
|
+
stat = fs21.lstatSync(full);
|
|
4534
4691
|
} catch {
|
|
4535
4692
|
return false;
|
|
4536
4693
|
}
|
|
@@ -4546,15 +4703,15 @@ function validateExtractedTree(destDir) {
|
|
|
4546
4703
|
}
|
|
4547
4704
|
function resolveTarBin() {
|
|
4548
4705
|
if (process.platform !== "win32") return "tar";
|
|
4549
|
-
const winTar =
|
|
4550
|
-
return
|
|
4706
|
+
const winTar = path28.join(process.env["SystemRoot"] ?? "C:\\Windows", "System32", "tar.exe");
|
|
4707
|
+
return fs21.existsSync(winTar) ? winTar : "tar";
|
|
4551
4708
|
}
|
|
4552
4709
|
async function downloadRepoTarball(repo, sha, destDir, validateSubdir) {
|
|
4553
4710
|
const url = `https://codeload.github.com/${repo}/tar.gz/${sha}`;
|
|
4554
|
-
const tmp =
|
|
4711
|
+
const tmp = path28.join(os2.tmpdir(), `jorgex-tarball-${Date.now()}.tar.gz`);
|
|
4555
4712
|
const fail = (reason) => {
|
|
4556
4713
|
try {
|
|
4557
|
-
|
|
4714
|
+
fs21.rmSync(destDir, { recursive: true, force: true });
|
|
4558
4715
|
} catch {
|
|
4559
4716
|
}
|
|
4560
4717
|
return { ok: false, reason };
|
|
@@ -4573,10 +4730,10 @@ async function downloadRepoTarball(repo, sha, destDir, validateSubdir) {
|
|
|
4573
4730
|
if (!res.body) return fail("respuesta HTTP sin cuerpo");
|
|
4574
4731
|
await pipeline(
|
|
4575
4732
|
Readable.fromWeb(res.body),
|
|
4576
|
-
|
|
4733
|
+
fs21.createWriteStream(tmp)
|
|
4577
4734
|
);
|
|
4578
|
-
|
|
4579
|
-
|
|
4735
|
+
fs21.rmSync(destDir, { recursive: true, force: true });
|
|
4736
|
+
fs21.mkdirSync(destDir, { recursive: true });
|
|
4580
4737
|
try {
|
|
4581
4738
|
execFileSync3(resolveTarBin(), ["-xzf", tmp, "--strip-components=1", "-C", destDir], { stdio: "pipe" });
|
|
4582
4739
|
} catch (err) {
|
|
@@ -4584,30 +4741,30 @@ async function downloadRepoTarball(repo, sha, destDir, validateSubdir) {
|
|
|
4584
4741
|
const detail = (e.stderr?.toString().trim() || e.message || "").split("\n")[0];
|
|
4585
4742
|
return fail(detail ? `tar fall\xF3: ${detail}` : "tar no disponible o fall\xF3 la extracci\xF3n");
|
|
4586
4743
|
}
|
|
4587
|
-
const resolvedDest =
|
|
4588
|
-
const validateRoot = validateSubdir ?
|
|
4744
|
+
const resolvedDest = path28.resolve(destDir);
|
|
4745
|
+
const validateRoot = validateSubdir ? path28.resolve(resolvedDest, validateSubdir) : resolvedDest;
|
|
4589
4746
|
if (validateRoot !== resolvedDest && !isContainedIn(validateRoot, resolvedDest)) {
|
|
4590
4747
|
return fail(`la ruta de validaci\xF3n "${validateSubdir}" escapa del destino`);
|
|
4591
4748
|
}
|
|
4592
|
-
if (
|
|
4749
|
+
if (fs21.existsSync(validateRoot) && !validateExtractedTree(validateRoot)) {
|
|
4593
4750
|
return fail("el \xE1rbol extra\xEDdo contiene symlinks o rutas fuera del destino");
|
|
4594
4751
|
}
|
|
4595
|
-
const validated =
|
|
4752
|
+
const validated = fs21.existsSync(validateRoot);
|
|
4596
4753
|
return { ok: true, validated };
|
|
4597
4754
|
} catch (err) {
|
|
4598
4755
|
const timedOut = err instanceof Error && (err.name === "TimeoutError" || err.name === "AbortError");
|
|
4599
4756
|
return fail(timedOut ? "timeout de descarga (120s)" : err instanceof Error ? `fallo de red: ${err.message}` : "error desconocido");
|
|
4600
4757
|
} finally {
|
|
4601
4758
|
try {
|
|
4602
|
-
|
|
4759
|
+
fs21.rmSync(tmp, { force: true });
|
|
4603
4760
|
} catch {
|
|
4604
4761
|
}
|
|
4605
4762
|
}
|
|
4606
4763
|
}
|
|
4607
4764
|
|
|
4608
4765
|
// src/lib/skill-update.ts
|
|
4609
|
-
import
|
|
4610
|
-
import
|
|
4766
|
+
import fs22 from "fs";
|
|
4767
|
+
import path29 from "path";
|
|
4611
4768
|
import { execFileSync as execFileSync4 } from "child_process";
|
|
4612
4769
|
var PROTECTED_SKILLS = /* @__PURE__ */ new Set([
|
|
4613
4770
|
"agent-delegation",
|
|
@@ -4618,8 +4775,8 @@ var PROTECTED_SKILLS = /* @__PURE__ */ new Set([
|
|
|
4618
4775
|
"xreview"
|
|
4619
4776
|
]);
|
|
4620
4777
|
function sameTextContentNormalized(a, b) {
|
|
4621
|
-
const ba =
|
|
4622
|
-
const bb =
|
|
4778
|
+
const ba = fs22.readFileSync(a);
|
|
4779
|
+
const bb = fs22.readFileSync(b);
|
|
4623
4780
|
if (ba.equals(bb)) return true;
|
|
4624
4781
|
const sa = ba.toString("utf8").replace(/\r\n/g, "\n");
|
|
4625
4782
|
const sb = bb.toString("utf8").replace(/\r\n/g, "\n");
|
|
@@ -4627,10 +4784,10 @@ function sameTextContentNormalized(a, b) {
|
|
|
4627
4784
|
}
|
|
4628
4785
|
function diffSkillDirs(upstreamDir, localDir) {
|
|
4629
4786
|
const upstreamFiles = new Set(
|
|
4630
|
-
listFilesRecursive(upstreamDir).map((f) =>
|
|
4787
|
+
listFilesRecursive(upstreamDir).map((f) => path29.relative(upstreamDir, f))
|
|
4631
4788
|
);
|
|
4632
4789
|
const localFiles = new Set(
|
|
4633
|
-
listFilesRecursive(localDir).map((f) =>
|
|
4790
|
+
listFilesRecursive(localDir).map((f) => path29.relative(localDir, f))
|
|
4634
4791
|
);
|
|
4635
4792
|
const added = [];
|
|
4636
4793
|
const modified = [];
|
|
@@ -4638,7 +4795,7 @@ function diffSkillDirs(upstreamDir, localDir) {
|
|
|
4638
4795
|
for (const rel of upstreamFiles) {
|
|
4639
4796
|
if (!localFiles.has(rel)) {
|
|
4640
4797
|
added.push(rel);
|
|
4641
|
-
} else if (!sameTextContentNormalized(
|
|
4798
|
+
} else if (!sameTextContentNormalized(path29.join(upstreamDir, rel), path29.join(localDir, rel))) {
|
|
4642
4799
|
modified.push(rel);
|
|
4643
4800
|
}
|
|
4644
4801
|
}
|
|
@@ -4700,8 +4857,8 @@ function replaceSkill(name, upstreamSkillDir, newCommit, opts) {
|
|
|
4700
4857
|
if (PROTECTED_SKILLS.has(name)) {
|
|
4701
4858
|
throw new Error(`La skill "${name}" es propia del stack y no se actualiza desde upstream.`);
|
|
4702
4859
|
}
|
|
4703
|
-
const upstreamsFile = upstreamsFilePath ??
|
|
4704
|
-
const raw =
|
|
4860
|
+
const upstreamsFile = upstreamsFilePath ?? path29.join(path29.dirname(stackRoot()), "upstreams.json");
|
|
4861
|
+
const raw = fs22.readFileSync(upstreamsFile, "utf8");
|
|
4705
4862
|
const data = JSON.parse(raw);
|
|
4706
4863
|
const skillEntry = data?.skills?.[name];
|
|
4707
4864
|
if (!skillEntry) {
|
|
@@ -4710,8 +4867,8 @@ function replaceSkill(name, upstreamSkillDir, newCommit, opts) {
|
|
|
4710
4867
|
if (skillEntry.kind === "release") {
|
|
4711
4868
|
throw new Error(`La skill "${name}" es de tipo release y no se actualiza con replaceSkill.`);
|
|
4712
4869
|
}
|
|
4713
|
-
const skillsRoot = localSkillsRoot ??
|
|
4714
|
-
const localSkillDir =
|
|
4870
|
+
const skillsRoot = localSkillsRoot ?? path29.join(stackRoot(), "skills");
|
|
4871
|
+
const localSkillDir = path29.join(skillsRoot, name);
|
|
4715
4872
|
const localFiles = listFilesRecursive(localSkillDir);
|
|
4716
4873
|
if (localFiles.length > 0) {
|
|
4717
4874
|
createBackup(localFiles, `skill-update-${name}`, backupsRoot2);
|
|
@@ -4720,24 +4877,24 @@ function replaceSkill(name, upstreamSkillDir, newCommit, opts) {
|
|
|
4720
4877
|
try {
|
|
4721
4878
|
const upstreamFiles = listFilesRecursive(upstreamSkillDir);
|
|
4722
4879
|
for (const src of upstreamFiles) {
|
|
4723
|
-
const st =
|
|
4880
|
+
const st = fs22.lstatSync(src);
|
|
4724
4881
|
if (st.isSymbolicLink()) {
|
|
4725
4882
|
throw new Error(`Symlink rechazado en upstream de skill "${name}": ${src}`);
|
|
4726
4883
|
}
|
|
4727
|
-
const rel =
|
|
4728
|
-
const dest =
|
|
4729
|
-
ensureDir(
|
|
4884
|
+
const rel = path29.relative(upstreamSkillDir, src);
|
|
4885
|
+
const dest = path29.join(stagingDir, rel);
|
|
4886
|
+
ensureDir(path29.dirname(dest));
|
|
4730
4887
|
copyFile(src, dest);
|
|
4731
4888
|
}
|
|
4732
4889
|
const oldDir = `${localSkillDir}.old-${process.pid}`;
|
|
4733
|
-
if (
|
|
4734
|
-
|
|
4890
|
+
if (fs22.existsSync(localSkillDir)) {
|
|
4891
|
+
fs22.renameSync(localSkillDir, oldDir);
|
|
4735
4892
|
}
|
|
4736
|
-
|
|
4737
|
-
|
|
4893
|
+
fs22.renameSync(stagingDir, localSkillDir);
|
|
4894
|
+
fs22.rmSync(oldDir, { recursive: true, force: true });
|
|
4738
4895
|
} catch (err) {
|
|
4739
4896
|
try {
|
|
4740
|
-
|
|
4897
|
+
fs22.rmSync(stagingDir, { recursive: true, force: true });
|
|
4741
4898
|
} catch {
|
|
4742
4899
|
}
|
|
4743
4900
|
throw err;
|
|
@@ -4751,8 +4908,8 @@ function rateLimitHint(prefix) {
|
|
|
4751
4908
|
return ghPresentButTokenFailed() ? `${prefix} Tienes gh instalado pero \`gh auth token\` no devolvi\xF3 credencial (\xBFsesi\xF3n caducada?) \u2014 prueba \`gh auth login\` o define GH_TOKEN.` : `${prefix} Define GH_TOKEN o inicia sesi\xF3n en gh CLI.`;
|
|
4752
4909
|
}
|
|
4753
4910
|
function loadUpstreams() {
|
|
4754
|
-
const file =
|
|
4755
|
-
return JSON.parse(
|
|
4911
|
+
const file = path30.join(path30.dirname(stackRoot()), "upstreams.json");
|
|
4912
|
+
return JSON.parse(fs23.readFileSync(file, "utf8"));
|
|
4756
4913
|
}
|
|
4757
4914
|
function skillsToScan(maintainer, upstreams) {
|
|
4758
4915
|
return maintainer ? Object.keys(upstreams.skills) : [];
|
|
@@ -4901,8 +5058,8 @@ function isEngramRunning() {
|
|
|
4901
5058
|
return null;
|
|
4902
5059
|
}
|
|
4903
5060
|
}
|
|
4904
|
-
function isGitClone(projectRoot =
|
|
4905
|
-
return
|
|
5061
|
+
function isGitClone(projectRoot = path30.dirname(stackRoot())) {
|
|
5062
|
+
return fs23.existsSync(path30.join(projectRoot, ".git"));
|
|
4906
5063
|
}
|
|
4907
5064
|
var STACK_METHOD_CLONE = "git pull + pnpm install + pnpm build";
|
|
4908
5065
|
function resolvePnpm() {
|
|
@@ -4912,12 +5069,12 @@ function resolvePnpm() {
|
|
|
4912
5069
|
}
|
|
4913
5070
|
function cleanupTmp(dir) {
|
|
4914
5071
|
try {
|
|
4915
|
-
|
|
5072
|
+
fs23.rmSync(dir, { recursive: true, force: true });
|
|
4916
5073
|
} catch {
|
|
4917
5074
|
}
|
|
4918
5075
|
}
|
|
4919
5076
|
function updateStackGitClone() {
|
|
4920
|
-
const projectRoot =
|
|
5077
|
+
const projectRoot = path30.dirname(stackRoot());
|
|
4921
5078
|
const git = lookPath("git");
|
|
4922
5079
|
if (!git) throw new Error("git no encontrado en PATH.");
|
|
4923
5080
|
const pnpm = resolvePnpm();
|
|
@@ -4934,7 +5091,7 @@ function updateStackGlobal() {
|
|
|
4934
5091
|
execFileSync5(pnpm, ["add", "-g", "jorgex-stack@latest"], { stdio: "inherit" });
|
|
4935
5092
|
}
|
|
4936
5093
|
async function downloadSkillToTemp(repo, sha, skillPath) {
|
|
4937
|
-
const root =
|
|
5094
|
+
const root = fs23.mkdtempSync(path30.join(os3.tmpdir(), "jorgex-skill-"));
|
|
4938
5095
|
try {
|
|
4939
5096
|
const result = await downloadRepoTarball(repo, sha, root, skillPath);
|
|
4940
5097
|
if (!result.ok) {
|
|
@@ -4942,15 +5099,15 @@ async function downloadSkillToTemp(repo, sha, skillPath) {
|
|
|
4942
5099
|
return { error: result.reason };
|
|
4943
5100
|
}
|
|
4944
5101
|
if (skillPath) {
|
|
4945
|
-
const sub =
|
|
5102
|
+
const sub = path30.resolve(path30.join(root, skillPath));
|
|
4946
5103
|
if (!isContainedIn(sub, root)) {
|
|
4947
5104
|
cleanupTmp(root);
|
|
4948
5105
|
return { error: `la ruta "${skillPath}" escapa del directorio temporal` };
|
|
4949
5106
|
}
|
|
4950
|
-
if (
|
|
5107
|
+
if (fs23.existsSync(sub)) return { dir: sub, root };
|
|
4951
5108
|
const lastSeg = skillPath.split("/").pop();
|
|
4952
|
-
const sub2 =
|
|
4953
|
-
if (isContainedIn(sub2, root) &&
|
|
5109
|
+
const sub2 = path30.resolve(path30.join(root, lastSeg));
|
|
5110
|
+
if (isContainedIn(sub2, root) && fs23.existsSync(sub2)) {
|
|
4954
5111
|
if (!validateExtractedTree(sub2)) {
|
|
4955
5112
|
cleanupTmp(root);
|
|
4956
5113
|
return { error: `el sub\xE1rbol "${lastSeg}" contiene symlinks o rutas fuera del destino` };
|
|
@@ -4969,11 +5126,11 @@ async function downloadSkillToTemp(repo, sha, skillPath) {
|
|
|
4969
5126
|
function pruneEngramDbBackups() {
|
|
4970
5127
|
try {
|
|
4971
5128
|
const dir = dataDir();
|
|
4972
|
-
if (!
|
|
4973
|
-
const backups =
|
|
5129
|
+
if (!fs23.existsSync(dir)) return;
|
|
5130
|
+
const backups = fs23.readdirSync(dir).filter((f) => f.startsWith("engram-db-backup-") && f.endsWith(".db")).map((f) => ({ name: f, mtime: fs23.statSync(path30.join(dir, f)).mtimeMs })).sort((a, b) => b.mtime - a.mtime);
|
|
4974
5131
|
for (const old of backups.slice(3)) {
|
|
4975
5132
|
try {
|
|
4976
|
-
|
|
5133
|
+
fs23.rmSync(path30.join(dir, old.name));
|
|
4977
5134
|
} catch {
|
|
4978
5135
|
}
|
|
4979
5136
|
}
|
|
@@ -4981,18 +5138,18 @@ function pruneEngramDbBackups() {
|
|
|
4981
5138
|
}
|
|
4982
5139
|
}
|
|
4983
5140
|
function rotateLockedBinary(binPath, sweepRoot = HOME) {
|
|
4984
|
-
if (!
|
|
4985
|
-
const dir =
|
|
4986
|
-
const base =
|
|
5141
|
+
if (!fs23.existsSync(binPath)) return null;
|
|
5142
|
+
const dir = path30.dirname(binPath);
|
|
5143
|
+
const base = path30.basename(binPath);
|
|
4987
5144
|
const escapedBase = base.replace(/[.*+?^$()|[\]{}\\]/g, "\\$&");
|
|
4988
5145
|
const oldPattern = new RegExp("^" + escapedBase + "\\.old-\\d+$");
|
|
4989
|
-
const resolvedDir =
|
|
4990
|
-
if (resolvedDir ===
|
|
5146
|
+
const resolvedDir = path30.resolve(dir);
|
|
5147
|
+
if (resolvedDir === path30.resolve(sweepRoot) || isContainedIn(resolvedDir, sweepRoot)) {
|
|
4991
5148
|
try {
|
|
4992
|
-
for (const entry of
|
|
5149
|
+
for (const entry of fs23.readdirSync(dir)) {
|
|
4993
5150
|
if (oldPattern.test(entry)) {
|
|
4994
5151
|
try {
|
|
4995
|
-
|
|
5152
|
+
fs23.rmSync(path30.join(dir, entry), { force: true });
|
|
4996
5153
|
} catch {
|
|
4997
5154
|
}
|
|
4998
5155
|
}
|
|
@@ -5000,8 +5157,8 @@ function rotateLockedBinary(binPath, sweepRoot = HOME) {
|
|
|
5000
5157
|
} catch {
|
|
5001
5158
|
}
|
|
5002
5159
|
}
|
|
5003
|
-
const rotated =
|
|
5004
|
-
|
|
5160
|
+
const rotated = path30.join(dir, `${base}.old-${Date.now()}`);
|
|
5161
|
+
fs23.renameSync(binPath, rotated);
|
|
5005
5162
|
return rotated;
|
|
5006
5163
|
}
|
|
5007
5164
|
async function updateEngram(engramRepo, latestVersion) {
|
|
@@ -5010,19 +5167,19 @@ async function updateEngram(engramRepo, latestVersion) {
|
|
|
5010
5167
|
"Engram est\xE1 en ejecuci\xF3n: los procesos vivos seguir\xE1n usando la versi\xF3n antigua hasta que reinicies los clientes (Claude Code/OpenCode/Codex)."
|
|
5011
5168
|
);
|
|
5012
5169
|
}
|
|
5013
|
-
const engramDataDir = process.env.ENGRAM_DATA_DIR ??
|
|
5014
|
-
const engramDb =
|
|
5015
|
-
if (
|
|
5170
|
+
const engramDataDir = process.env.ENGRAM_DATA_DIR ?? path30.join(HOME, ".engram");
|
|
5171
|
+
const engramDb = path30.join(engramDataDir, "engram.db");
|
|
5172
|
+
if (fs23.existsSync(engramDb)) {
|
|
5016
5173
|
const doBackup = await p4.confirm({
|
|
5017
5174
|
message: `\xBFHacer backup de la DB de Engram antes de actualizar? (${engramDb})`,
|
|
5018
5175
|
initialValue: true
|
|
5019
5176
|
});
|
|
5020
5177
|
if (!p4.isCancel(doBackup) && doBackup) {
|
|
5021
5178
|
const ts = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
5022
|
-
const dest =
|
|
5179
|
+
const dest = path30.join(dataDir(), `engram-db-backup-${ts}.db`);
|
|
5023
5180
|
try {
|
|
5024
|
-
if (!
|
|
5025
|
-
|
|
5181
|
+
if (!fs23.existsSync(dataDir())) fs23.mkdirSync(dataDir(), { recursive: true });
|
|
5182
|
+
fs23.copyFileSync(engramDb, dest);
|
|
5026
5183
|
p4.log.success(`DB respaldada en ${dest} (la DB original NO se modifica jam\xE1s).`);
|
|
5027
5184
|
pruneEngramDbBackups();
|
|
5028
5185
|
} catch (err) {
|
|
@@ -5072,10 +5229,10 @@ async function updateEngram(engramRepo, latestVersion) {
|
|
|
5072
5229
|
["install", `github.com/Gentleman-Programming/engram/cmd/engram@v${latestVersion}`],
|
|
5073
5230
|
{ stdio: "inherit" }
|
|
5074
5231
|
);
|
|
5075
|
-
const rollbackOk = resolveEngramRollback({ installOk: true, rotated, bin, binExists:
|
|
5232
|
+
const rollbackOk = resolveEngramRollback({ installOk: true, rotated, bin, binExists: fs23.existsSync(bin ?? "") });
|
|
5076
5233
|
if (rollbackOk.action === "restore") {
|
|
5077
5234
|
try {
|
|
5078
|
-
|
|
5235
|
+
fs23.renameSync(rotated, bin);
|
|
5079
5236
|
p4.log.warn(rollbackOk.messages.onRestore);
|
|
5080
5237
|
} catch {
|
|
5081
5238
|
p4.log.warn(rollbackOk.messages.onRenameFail);
|
|
@@ -5083,10 +5240,10 @@ async function updateEngram(engramRepo, latestVersion) {
|
|
|
5083
5240
|
}
|
|
5084
5241
|
return true;
|
|
5085
5242
|
} catch (err) {
|
|
5086
|
-
const rollbackFail = resolveEngramRollback({ installOk: false, rotated, bin, binExists:
|
|
5243
|
+
const rollbackFail = resolveEngramRollback({ installOk: false, rotated, bin, binExists: fs23.existsSync(bin ?? "") });
|
|
5087
5244
|
if (rollbackFail.action === "restore") {
|
|
5088
5245
|
try {
|
|
5089
|
-
|
|
5246
|
+
fs23.renameSync(rotated, bin);
|
|
5090
5247
|
p4.log.info(rollbackFail.messages.onRestore);
|
|
5091
5248
|
} catch {
|
|
5092
5249
|
p4.log.error(rollbackFail.messages.onRenameFail);
|
|
@@ -5183,6 +5340,7 @@ async function runInteractiveUpdate(localVersion, yes, dryRun = false, includeBr
|
|
|
5183
5340
|
let exitCode = 0;
|
|
5184
5341
|
let appliedUpdates = false;
|
|
5185
5342
|
const updated = [];
|
|
5343
|
+
let playwrightCapability;
|
|
5186
5344
|
const maintainer = isGitClone();
|
|
5187
5345
|
const spin = p4.spinner();
|
|
5188
5346
|
spin.start("Consultando versiones upstream\u2026");
|
|
@@ -5356,7 +5514,7 @@ async function runInteractiveUpdate(localVersion, yes, dryRun = false, includeBr
|
|
|
5356
5514
|
continue;
|
|
5357
5515
|
}
|
|
5358
5516
|
const { dir: tmpDir, root: tmpRoot } = tmpResult;
|
|
5359
|
-
const localSkillDir =
|
|
5517
|
+
const localSkillDir = path30.join(stackRoot(), "skills", skillInfo.name);
|
|
5360
5518
|
const diff = renderSkillDiff(tmpDir, localSkillDir);
|
|
5361
5519
|
if (diff) {
|
|
5362
5520
|
p4.log.info(`Diff de ${skillInfo.name}:
|
|
@@ -5402,6 +5560,7 @@ ${diff}`);
|
|
|
5402
5560
|
p4.log.success("Playwright CLI actualizado al pin aprobado.");
|
|
5403
5561
|
appliedUpdates = true;
|
|
5404
5562
|
updated.push("playwright-cli");
|
|
5563
|
+
playwrightCapability = inspectPlaywrightCapability({ browserVerified: true });
|
|
5405
5564
|
} else {
|
|
5406
5565
|
const failedResult = packageResult.ok ? browserResult : packageResult;
|
|
5407
5566
|
const pnpmRemedy = failedResult && !failedResult.ok ? resolvePnpmFailureRemedy(failedResult.reason) : null;
|
|
@@ -5438,11 +5597,16 @@ ${diff}`);
|
|
|
5438
5597
|
}
|
|
5439
5598
|
}
|
|
5440
5599
|
p4.outro(exitCode === 0 ? "Update completado." : "Update completado con errores (revisa arriba).");
|
|
5441
|
-
return {
|
|
5600
|
+
return {
|
|
5601
|
+
exitCode,
|
|
5602
|
+
appliedUpdates,
|
|
5603
|
+
syncRequired: resolveUpdateSyncRequired(updated),
|
|
5604
|
+
...playwrightCapability === void 0 ? {} : { playwrightCapability }
|
|
5605
|
+
};
|
|
5442
5606
|
}
|
|
5443
5607
|
|
|
5444
5608
|
// src/models-picker.ts
|
|
5445
|
-
import
|
|
5609
|
+
import path31 from "path";
|
|
5446
5610
|
import * as p5 from "@clack/prompts";
|
|
5447
5611
|
var TIERS = ["strong", "standard", "cheap"];
|
|
5448
5612
|
var EFFORTS = ["low", "medium", "high", "xhigh"];
|
|
@@ -5468,7 +5632,7 @@ function opencodeLiveModels(binPath) {
|
|
|
5468
5632
|
}
|
|
5469
5633
|
function agentsByTier() {
|
|
5470
5634
|
const grouped = { strong: [], standard: [], cheap: [] };
|
|
5471
|
-
for (const agent of loadCanonicalAgents(
|
|
5635
|
+
for (const agent of loadCanonicalAgents(path31.join(stackRoot(), "agents"))) {
|
|
5472
5636
|
if (agent.mode === "subagent") grouped[agent.tier].push(agent.name);
|
|
5473
5637
|
}
|
|
5474
5638
|
return grouped;
|
|
@@ -5653,16 +5817,16 @@ function cancelled() {
|
|
|
5653
5817
|
}
|
|
5654
5818
|
|
|
5655
5819
|
// src/lib/release.ts
|
|
5656
|
-
import
|
|
5657
|
-
import
|
|
5820
|
+
import fs24 from "fs";
|
|
5821
|
+
import path32 from "path";
|
|
5658
5822
|
import { execFileSync as execFileSync6 } from "child_process";
|
|
5659
5823
|
import { fileURLToPath } from "url";
|
|
5660
5824
|
function findPackageJson() {
|
|
5661
|
-
let dir =
|
|
5825
|
+
let dir = path32.dirname(fileURLToPath(import.meta.url));
|
|
5662
5826
|
for (let i = 0; i < 6; i++) {
|
|
5663
|
-
const candidate =
|
|
5664
|
-
if (
|
|
5665
|
-
dir =
|
|
5827
|
+
const candidate = path32.join(dir, "package.json");
|
|
5828
|
+
if (fs24.existsSync(candidate)) return candidate;
|
|
5829
|
+
dir = path32.dirname(dir);
|
|
5666
5830
|
}
|
|
5667
5831
|
throw new Error("No se encontr\xF3 package.json cerca del CLI.");
|
|
5668
5832
|
}
|
|
@@ -5671,7 +5835,7 @@ function readPackageVersion() {
|
|
|
5671
5835
|
}
|
|
5672
5836
|
function readPackageMetadata() {
|
|
5673
5837
|
const packageJson = findPackageJson();
|
|
5674
|
-
const raw =
|
|
5838
|
+
const raw = fs24.readFileSync(packageJson, "utf8");
|
|
5675
5839
|
const parsed = JSON.parse(raw);
|
|
5676
5840
|
const name = typeof parsed.name === "string" ? parsed.name.trim() : "";
|
|
5677
5841
|
const version = typeof parsed.version === "string" ? parsed.version.trim() : "";
|
|
@@ -5683,8 +5847,8 @@ function readPackageMetadata() {
|
|
|
5683
5847
|
|
|
5684
5848
|
// src/lib/pi-runtime.ts
|
|
5685
5849
|
import os4 from "os";
|
|
5686
|
-
import
|
|
5687
|
-
import
|
|
5850
|
+
import path33 from "path";
|
|
5851
|
+
import fs25 from "fs";
|
|
5688
5852
|
import { spawnSync } from "child_process";
|
|
5689
5853
|
import { createHash as createHash2 } from "crypto";
|
|
5690
5854
|
|
|
@@ -5851,14 +6015,14 @@ function healthyDoctor(stdout, stderr, packageRunner, candidate) {
|
|
|
5851
6015
|
if (record === null || typeof record !== "object" || Array.isArray(record)) return false;
|
|
5852
6016
|
const packageValue = Reflect.get(record, "package");
|
|
5853
6017
|
const result = Reflect.get(record, "result");
|
|
5854
|
-
return Reflect.get(record, "schemaVersion") === 1 && Reflect.get(record, "command") === "doctor" && Reflect.get(record, "ok") === true && packageValue !== null && typeof packageValue === "object" && Reflect.get(packageValue, "name") === "jorgex-pi" && Reflect.get(packageValue, "version") === (candidate.package?.version ?? /^npm:jorgex-pi@([^\s]+)$/.exec(candidate.source)?.[1]) &&
|
|
6018
|
+
return Reflect.get(record, "schemaVersion") === 1 && Reflect.get(record, "command") === "doctor" && Reflect.get(record, "ok") === true && packageValue !== null && typeof packageValue === "object" && Reflect.get(packageValue, "name") === "jorgex-pi" && Reflect.get(packageValue, "version") === (candidate.package?.version ?? /^npm:jorgex-pi@([^\s]+)$/.exec(candidate.source)?.[1]) && path33.resolve(packageRunner) === path33.resolve(String(Reflect.get(packageValue, "root")), "bin", "jorgex-pi.mjs") && result !== null && typeof result === "object" && Reflect.get(result, "healthy") === true;
|
|
5855
6019
|
} catch {
|
|
5856
6020
|
return false;
|
|
5857
6021
|
}
|
|
5858
6022
|
}
|
|
5859
6023
|
function installPiFromVerifiedTarball(input, deps) {
|
|
5860
6024
|
const paths = input.targetDir === void 0 ? userPaths(input.engramBin, input.piExecutable) : targetPaths(input.targetDir, input.engramBin, input.piExecutable);
|
|
5861
|
-
const destination = input.targetDir === void 0 ?
|
|
6025
|
+
const destination = input.targetDir === void 0 ? path33.join(dataDir(), "packages", `jorgex-pi-${PI_RUNTIME_CANDIDATE.package.version}.tgz`) : path33.join(path33.resolve(input.targetDir), "downloads", `jorgex-pi-${PI_RUNTIME_CANDIDATE.package.version}.tgz`);
|
|
5862
6026
|
const artifact = deps.download(destination);
|
|
5863
6027
|
if (artifact.bytes !== input.candidate.bytes || artifact.sha256 !== input.candidate.sha256 || artifact.sha512 !== input.candidate.sha512) {
|
|
5864
6028
|
return { kind: "blocked", reason: "tarball-integrity" };
|
|
@@ -5866,7 +6030,7 @@ function installPiFromVerifiedTarball(input, deps) {
|
|
|
5866
6030
|
deps.backupSettings();
|
|
5867
6031
|
const scope = {
|
|
5868
6032
|
kind: input.targetDir === void 0 ? "real" : "target-dir",
|
|
5869
|
-
codingAgentDir:
|
|
6033
|
+
codingAgentDir: path33.resolve(paths.codingAgentDir)
|
|
5870
6034
|
};
|
|
5871
6035
|
const installing = flatCandidateReceipt(input.candidate, scope, "installing", input.engramBin);
|
|
5872
6036
|
deps.writeReceiptAtomic(`${JSON.stringify(installing)}
|
|
@@ -5895,30 +6059,30 @@ function installPiFromVerifiedTarball(input, deps) {
|
|
|
5895
6059
|
return { kind: "installed", receipt };
|
|
5896
6060
|
}
|
|
5897
6061
|
function runtimePath(piExecutable) {
|
|
5898
|
-
const entries = process.platform === "win32" ? [piExecutable === void 0 ? null :
|
|
5899
|
-
return [...new Set(entries.filter((entry) => entry !== null))].join(
|
|
6062
|
+
const entries = process.platform === "win32" ? [piExecutable === void 0 ? null : path33.dirname(piExecutable), path33.dirname(process.execPath), process.env.SystemRoot ? path33.join(process.env.SystemRoot, "System32") : null] : [piExecutable === void 0 ? null : path33.dirname(piExecutable), path33.dirname(process.execPath), "/usr/local/bin", "/usr/bin", "/bin"];
|
|
6063
|
+
return [...new Set(entries.filter((entry) => entry !== null))].join(path33.delimiter);
|
|
5900
6064
|
}
|
|
5901
6065
|
function targetPaths(targetDir, engramBin, piExecutable) {
|
|
5902
|
-
const root =
|
|
5903
|
-
const codingAgentDir =
|
|
5904
|
-
const home =
|
|
5905
|
-
const temporary =
|
|
6066
|
+
const root = path33.resolve(targetDir);
|
|
6067
|
+
const codingAgentDir = path33.join(root, "pi-agent");
|
|
6068
|
+
const home = path33.join(root, "home");
|
|
6069
|
+
const temporary = path33.join(root, "tmp");
|
|
5906
6070
|
return {
|
|
5907
6071
|
codingAgentDir,
|
|
5908
|
-
receiptPath:
|
|
5909
|
-
packageRunner:
|
|
6072
|
+
receiptPath: path33.join(root, "state", "pi-receipt.json"),
|
|
6073
|
+
packageRunner: path33.join(codingAgentDir, "npm", "node_modules", "jorgex-pi", "bin", "jorgex-pi.mjs"),
|
|
5910
6074
|
environment: {
|
|
5911
6075
|
HOME: home,
|
|
5912
6076
|
USERPROFILE: home,
|
|
5913
|
-
APPDATA:
|
|
5914
|
-
LOCALAPPDATA:
|
|
5915
|
-
XDG_CONFIG_HOME:
|
|
5916
|
-
XDG_DATA_HOME:
|
|
5917
|
-
XDG_CACHE_HOME:
|
|
6077
|
+
APPDATA: path33.join(root, "appdata"),
|
|
6078
|
+
LOCALAPPDATA: path33.join(root, "localappdata"),
|
|
6079
|
+
XDG_CONFIG_HOME: path33.join(root, "xdg-config"),
|
|
6080
|
+
XDG_DATA_HOME: path33.join(root, "xdg-data"),
|
|
6081
|
+
XDG_CACHE_HOME: path33.join(root, "xdg-cache"),
|
|
5918
6082
|
TEMP: temporary,
|
|
5919
6083
|
TMP: temporary,
|
|
5920
6084
|
TMPDIR: temporary,
|
|
5921
|
-
npm_config_cache:
|
|
6085
|
+
npm_config_cache: path33.join(root, "npm-cache"),
|
|
5922
6086
|
NPM_CONFIG_IGNORE_SCRIPTS: "true",
|
|
5923
6087
|
NPM_CONFIG_UPDATE_NOTIFIER: "false",
|
|
5924
6088
|
PI_CODING_AGENT_DIR: codingAgentDir,
|
|
@@ -5929,15 +6093,15 @@ function targetPaths(targetDir, engramBin, piExecutable) {
|
|
|
5929
6093
|
}
|
|
5930
6094
|
function userPaths(engramBin, piExecutable) {
|
|
5931
6095
|
const home = os4.homedir();
|
|
5932
|
-
const codingAgentDir = process.env.PI_CODING_AGENT_DIR ??
|
|
6096
|
+
const codingAgentDir = process.env.PI_CODING_AGENT_DIR ?? path33.join(home, ".pi", "agent");
|
|
5933
6097
|
return {
|
|
5934
6098
|
codingAgentDir,
|
|
5935
|
-
receiptPath:
|
|
5936
|
-
packageRunner:
|
|
6099
|
+
receiptPath: path33.join(dataDir(), "pi-receipt.json"),
|
|
6100
|
+
packageRunner: path33.join(codingAgentDir, "npm", "node_modules", "jorgex-pi", "bin", "jorgex-pi.mjs"),
|
|
5937
6101
|
environment: {
|
|
5938
6102
|
HOME: home,
|
|
5939
|
-
XDG_CONFIG_HOME: process.env.XDG_CONFIG_HOME ??
|
|
5940
|
-
XDG_CACHE_HOME: process.env.XDG_CACHE_HOME ??
|
|
6103
|
+
XDG_CONFIG_HOME: process.env.XDG_CONFIG_HOME ?? path33.join(home, ".config"),
|
|
6104
|
+
XDG_CACHE_HOME: process.env.XDG_CACHE_HOME ?? path33.join(home, ".cache"),
|
|
5941
6105
|
TMPDIR: os4.tmpdir(),
|
|
5942
6106
|
NPM_CONFIG_IGNORE_SCRIPTS: "true",
|
|
5943
6107
|
NPM_CONFIG_UPDATE_NOTIFIER: "false",
|
|
@@ -5965,7 +6129,7 @@ function runPiRuntime(input, deps) {
|
|
|
5965
6129
|
return { kind: "blocked", reason: "tarball-integrity" };
|
|
5966
6130
|
}
|
|
5967
6131
|
const paths = input.targetDir === void 0 ? userPaths(input.engramBin, input.detected.executable) : targetPaths(input.targetDir, input.engramBin, input.detected.executable);
|
|
5968
|
-
const settingsJson = deps.readSettings(
|
|
6132
|
+
const settingsJson = deps.readSettings(path33.join(paths.codingAgentDir, "settings.json"));
|
|
5969
6133
|
const receiptJson = deps.readReceipt(paths.receiptPath);
|
|
5970
6134
|
const lifecycleInput = {
|
|
5971
6135
|
candidate: PI_RUNTIME_CANDIDATE,
|
|
@@ -6023,19 +6187,19 @@ function runPiRuntime(input, deps) {
|
|
|
6023
6187
|
return result;
|
|
6024
6188
|
}
|
|
6025
6189
|
function readJsonFile(file) {
|
|
6026
|
-
return JSON.parse(
|
|
6190
|
+
return JSON.parse(fs25.readFileSync(file, "utf8"));
|
|
6027
6191
|
}
|
|
6028
6192
|
function packageVersionFromExecutable(executable) {
|
|
6029
6193
|
let current;
|
|
6030
6194
|
try {
|
|
6031
|
-
current =
|
|
6195
|
+
current = path33.dirname(fs25.realpathSync(executable));
|
|
6032
6196
|
} catch {
|
|
6033
6197
|
return null;
|
|
6034
6198
|
}
|
|
6035
6199
|
for (let depth = 0; depth < 8; depth++) {
|
|
6036
6200
|
const manifests = [
|
|
6037
|
-
|
|
6038
|
-
|
|
6201
|
+
path33.join(current, "package.json"),
|
|
6202
|
+
path33.join(current, "node_modules", "@earendil-works", "pi-coding-agent", "package.json")
|
|
6039
6203
|
];
|
|
6040
6204
|
for (const manifest of manifests) {
|
|
6041
6205
|
try {
|
|
@@ -6046,7 +6210,7 @@ function packageVersionFromExecutable(executable) {
|
|
|
6046
6210
|
} catch {
|
|
6047
6211
|
}
|
|
6048
6212
|
}
|
|
6049
|
-
const parent =
|
|
6213
|
+
const parent = path33.dirname(current);
|
|
6050
6214
|
if (parent === current) break;
|
|
6051
6215
|
current = parent;
|
|
6052
6216
|
}
|
|
@@ -6061,35 +6225,35 @@ function detectPiRuntime() {
|
|
|
6061
6225
|
installed: executable !== null,
|
|
6062
6226
|
executable,
|
|
6063
6227
|
version: executable === null ? null : packageVersionFromExecutable(executable),
|
|
6064
|
-
codingAgentDir: process.env.PI_CODING_AGENT_DIR ??
|
|
6228
|
+
codingAgentDir: process.env.PI_CODING_AGENT_DIR ?? path33.join(home, ".pi", "agent")
|
|
6065
6229
|
};
|
|
6066
6230
|
}
|
|
6067
6231
|
function hasManagedPiRuntime(targetDir) {
|
|
6068
|
-
const receipt = targetDir === void 0 ?
|
|
6069
|
-
return
|
|
6232
|
+
const receipt = targetDir === void 0 ? path33.join(dataDir(), "pi-receipt.json") : path33.join(path33.resolve(targetDir), "state", "pi-receipt.json");
|
|
6233
|
+
return fs25.statSync(receipt, { throwIfNoEntry: false })?.isFile() === true;
|
|
6070
6234
|
}
|
|
6071
6235
|
function resolvePiEngramBin(targetDir) {
|
|
6072
6236
|
if (targetDir === void 0) return detectEngram();
|
|
6073
|
-
const candidate =
|
|
6074
|
-
return
|
|
6237
|
+
const candidate = path33.join(path33.resolve(targetDir), "bin", process.platform === "win32" ? "engram.exe" : "engram");
|
|
6238
|
+
return fs25.statSync(candidate, { throwIfNoEntry: false })?.isFile() ? candidate : null;
|
|
6075
6239
|
}
|
|
6076
6240
|
function readOptional(file, fallback) {
|
|
6077
6241
|
try {
|
|
6078
|
-
return
|
|
6242
|
+
return fs25.readFileSync(file, "utf8");
|
|
6079
6243
|
} catch (error) {
|
|
6080
6244
|
if (error.code === "ENOENT") return fallback;
|
|
6081
6245
|
throw error;
|
|
6082
6246
|
}
|
|
6083
6247
|
}
|
|
6084
6248
|
function hashPiTarball(file) {
|
|
6085
|
-
const descriptor =
|
|
6249
|
+
const descriptor = fs25.openSync(file, "r");
|
|
6086
6250
|
const sha2562 = createHash2("sha256");
|
|
6087
6251
|
const sha512 = createHash2("sha512");
|
|
6088
6252
|
const buffer = Buffer.allocUnsafe(1024 * 1024);
|
|
6089
6253
|
let bytes = 0;
|
|
6090
6254
|
try {
|
|
6091
6255
|
while (true) {
|
|
6092
|
-
const read =
|
|
6256
|
+
const read = fs25.readSync(descriptor, buffer, 0, buffer.length, null);
|
|
6093
6257
|
if (read === 0) break;
|
|
6094
6258
|
bytes += read;
|
|
6095
6259
|
const chunk = buffer.subarray(0, read);
|
|
@@ -6097,27 +6261,27 @@ function hashPiTarball(file) {
|
|
|
6097
6261
|
sha512.update(chunk);
|
|
6098
6262
|
}
|
|
6099
6263
|
} finally {
|
|
6100
|
-
|
|
6264
|
+
fs25.closeSync(descriptor);
|
|
6101
6265
|
}
|
|
6102
6266
|
return { path: file, bytes, sha256: sha2562.digest("hex"), sha512: sha512.digest("hex") };
|
|
6103
6267
|
}
|
|
6104
6268
|
async function acquirePiTarball(destination) {
|
|
6105
|
-
const existing =
|
|
6269
|
+
const existing = fs25.statSync(destination, { throwIfNoEntry: false });
|
|
6106
6270
|
if (existing?.isFile()) {
|
|
6107
6271
|
const observed = hashPiTarball(destination);
|
|
6108
6272
|
if (observed.bytes === PI_RUNTIME_CANDIDATE.tarball.bytes && observed.sha256 === PI_RUNTIME_CANDIDATE.tarball.sha256 && observed.sha512 === PI_RUNTIME_CANDIDATE.tarball.sha512) {
|
|
6109
6273
|
return observed;
|
|
6110
6274
|
}
|
|
6111
|
-
|
|
6275
|
+
fs25.rmSync(destination, { force: true });
|
|
6112
6276
|
}
|
|
6113
|
-
|
|
6277
|
+
fs25.mkdirSync(path33.dirname(destination), { recursive: true });
|
|
6114
6278
|
const partial = `${destination}.partial-${process.pid}`;
|
|
6115
6279
|
const response = await fetch(`https://registry.npmjs.org/jorgex-pi/-/jorgex-pi-${PI_RUNTIME_CANDIDATE.package.version}.tgz`, {
|
|
6116
6280
|
redirect: "error",
|
|
6117
6281
|
headers: { accept: "application/octet-stream" }
|
|
6118
6282
|
});
|
|
6119
6283
|
if (!response.ok || response.body === null) throw new Error(`No se pudo descargar jorgex-pi@${PI_RUNTIME_CANDIDATE.package.version} (${response.status}).`);
|
|
6120
|
-
const descriptor =
|
|
6284
|
+
const descriptor = fs25.openSync(partial, "wx", 384);
|
|
6121
6285
|
let bytes = 0;
|
|
6122
6286
|
try {
|
|
6123
6287
|
for await (const chunk of response.body) {
|
|
@@ -6125,15 +6289,15 @@ async function acquirePiTarball(destination) {
|
|
|
6125
6289
|
bytes += buffer.length;
|
|
6126
6290
|
if (bytes > PI_RUNTIME_CANDIDATE.tarball.bytes) throw new Error("El tarball de jorgex-pi excede el tama\xF1o fijado.");
|
|
6127
6291
|
let offset = 0;
|
|
6128
|
-
while (offset < buffer.length) offset +=
|
|
6292
|
+
while (offset < buffer.length) offset += fs25.writeSync(descriptor, buffer, offset);
|
|
6129
6293
|
}
|
|
6130
6294
|
} catch (error) {
|
|
6131
|
-
|
|
6132
|
-
|
|
6295
|
+
fs25.closeSync(descriptor);
|
|
6296
|
+
fs25.rmSync(partial, { force: true });
|
|
6133
6297
|
throw error;
|
|
6134
6298
|
}
|
|
6135
|
-
|
|
6136
|
-
|
|
6299
|
+
fs25.closeSync(descriptor);
|
|
6300
|
+
fs25.renameSync(partial, destination);
|
|
6137
6301
|
return hashPiTarball(destination);
|
|
6138
6302
|
}
|
|
6139
6303
|
function runProcess(invocation) {
|
|
@@ -6183,7 +6347,7 @@ async function runPiRuntimeSystem(input) {
|
|
|
6183
6347
|
remedy: "Instala Engram o configura un ENGRAM_BIN absoluto antes de reintentar."
|
|
6184
6348
|
};
|
|
6185
6349
|
}
|
|
6186
|
-
const destination = input.targetDir === void 0 ?
|
|
6350
|
+
const destination = input.targetDir === void 0 ? path33.join(dataDir(), "packages", `jorgex-pi-${PI_RUNTIME_CANDIDATE.package.version}.tgz`) : path33.join(path33.resolve(input.targetDir), "downloads", `jorgex-pi-${PI_RUNTIME_CANDIDATE.package.version}.tgz`);
|
|
6187
6351
|
let artifact;
|
|
6188
6352
|
try {
|
|
6189
6353
|
artifact = await acquirePiTarball(destination);
|
|
@@ -6203,18 +6367,18 @@ async function runPiRuntimeSystem(input) {
|
|
|
6203
6367
|
}, {
|
|
6204
6368
|
download: () => artifact,
|
|
6205
6369
|
backupSettings: () => createBackup(
|
|
6206
|
-
[
|
|
6370
|
+
[path33.join(paths.codingAgentDir, "settings.json")],
|
|
6207
6371
|
"pi-package-install",
|
|
6208
|
-
input.targetDir === void 0 ? void 0 :
|
|
6372
|
+
input.targetDir === void 0 ? void 0 : path33.join(path33.resolve(input.targetDir), "backups")
|
|
6209
6373
|
),
|
|
6210
6374
|
run: runProcess,
|
|
6211
|
-
readSettings: () => readOptional(
|
|
6212
|
-
rewriteSettings: (content) => writeText(
|
|
6375
|
+
readSettings: () => readOptional(path33.join(paths.codingAgentDir, "settings.json"), '{"packages":[]}'),
|
|
6376
|
+
rewriteSettings: (content) => writeText(path33.join(paths.codingAgentDir, "settings.json"), `${content}
|
|
6213
6377
|
`),
|
|
6214
6378
|
writeReceiptAtomic: (content) => writeText(paths.receiptPath, content)
|
|
6215
6379
|
});
|
|
6216
6380
|
}
|
|
6217
|
-
const packageRoot =
|
|
6381
|
+
const packageRoot = path33.dirname(path33.dirname(paths.packageRunner));
|
|
6218
6382
|
const writeReceipt = (receipt) => {
|
|
6219
6383
|
writeText(paths.receiptPath, `${JSON.stringify(receipt, null, 2)}
|
|
6220
6384
|
`);
|
|
@@ -6232,13 +6396,13 @@ async function runPiRuntimeSystem(input) {
|
|
|
6232
6396
|
value,
|
|
6233
6397
|
{
|
|
6234
6398
|
backupSettings: () => createBackup(
|
|
6235
|
-
[
|
|
6399
|
+
[path33.join(paths.codingAgentDir, "settings.json")],
|
|
6236
6400
|
"pi-package-uninstall",
|
|
6237
|
-
input.targetDir === void 0 ? void 0 :
|
|
6401
|
+
input.targetDir === void 0 ? void 0 : path33.join(path33.resolve(input.targetDir), "backups")
|
|
6238
6402
|
),
|
|
6239
6403
|
run: runProcess,
|
|
6240
|
-
isPackageAbsent: () => !
|
|
6241
|
-
deleteReceipt: () =>
|
|
6404
|
+
isPackageAbsent: () => !fs25.existsSync(packageRoot),
|
|
6405
|
+
deleteReceipt: () => fs25.rmSync(paths.receiptPath, { force: true })
|
|
6242
6406
|
}
|
|
6243
6407
|
)
|
|
6244
6408
|
};
|
|
@@ -6313,19 +6477,35 @@ async function runManagedPiSystem(input) {
|
|
|
6313
6477
|
const {
|
|
6314
6478
|
devtoolsMcpEnabled: explicitDevtools,
|
|
6315
6479
|
playwrightCliEnabled: explicitPlaywright,
|
|
6480
|
+
playwrightCapability,
|
|
6481
|
+
packageOnly,
|
|
6316
6482
|
writingStyle: suppliedStyle,
|
|
6317
6483
|
writingStyleMode,
|
|
6318
6484
|
...runtimeInput
|
|
6319
6485
|
} = input;
|
|
6486
|
+
if (input.operation === "doctor" && packageOnly) {
|
|
6487
|
+
const result2 = managedPackageResult(await runPiRuntimeSystem(runtimeInput));
|
|
6488
|
+
return result2.kind === "manual-existing" ? manualExistingResult(result2) : result2;
|
|
6489
|
+
}
|
|
6320
6490
|
const readsStyle = input.operation !== "uninstall" && input.operation !== "models";
|
|
6491
|
+
if (input.operation !== "models") {
|
|
6492
|
+
try {
|
|
6493
|
+
assertSystemPromptFile(piSystemPromptFile(input.targetDir), input.targetDir);
|
|
6494
|
+
} catch (error) {
|
|
6495
|
+
return { kind: "blocked", reason: "projection-prompt-markers", remedy: error instanceof Error ? error.message : String(error) };
|
|
6496
|
+
}
|
|
6497
|
+
}
|
|
6321
6498
|
const preparedStyle = readsStyle && suppliedStyle === void 0 ? prepareWritingStyle(resolveWritingStyleFile({ targetDir: input.targetDir }), { rootDir: input.targetDir }) : void 0;
|
|
6322
6499
|
const style = readsStyle ? suppliedStyle ?? preparedStyle : void 0;
|
|
6323
6500
|
const mode = readsStyle ? writingStyleMode ?? (input.targetDir === void 0 ? loadInstallModePreference().mode : "human") : "human";
|
|
6324
6501
|
const writingStyle = style && mode === "programmatic" ? { ...style, content: null } : style;
|
|
6325
6502
|
const devtoolsMcpEnabled = explicitDevtools ?? (input.targetDir === void 0 && loadDevtoolsMcpPreference(devtoolsMcpPreferenceFile(), "pi"));
|
|
6326
6503
|
const supportsPlaywright = PI_RUNTIME_CANDIDATE.contract.capabilities.includes("playwright-handoff-v1");
|
|
6327
|
-
const
|
|
6328
|
-
const
|
|
6504
|
+
const persistedPlaywright = input.targetDir === void 0 && loadPlaywrightCliPreference(void 0, "pi") === true;
|
|
6505
|
+
const selectedPlaywright = explicitPlaywright ?? persistedPlaywright;
|
|
6506
|
+
const playwrightCapabilityEffective = playwrightCapability?.effective === true;
|
|
6507
|
+
const playwrightCliEnabled = input.targetDir === void 0 && supportsPlaywright && selectedPlaywright && playwrightCapabilityEffective;
|
|
6508
|
+
const playwrightCliCommand = playwrightCliEnabled && input.operation !== "uninstall" && input.operation !== "models" ? playwrightCapability?.cli.binPath ?? null : null;
|
|
6329
6509
|
const projectionInput = {
|
|
6330
6510
|
writingStyle,
|
|
6331
6511
|
targetDir: input.targetDir,
|
|
@@ -6371,7 +6551,7 @@ async function runManagedPiSystem(input) {
|
|
|
6371
6551
|
}
|
|
6372
6552
|
|
|
6373
6553
|
// src/lib/quality-runner.ts
|
|
6374
|
-
import
|
|
6554
|
+
import path34 from "path";
|
|
6375
6555
|
import { spawn, spawnSync as spawnSync2 } from "child_process";
|
|
6376
6556
|
function normalizeLimit(value, name) {
|
|
6377
6557
|
if (value === void 0) return void 0;
|
|
@@ -6503,7 +6683,7 @@ function killProcessTree(child) {
|
|
|
6503
6683
|
if (pid === void 0) return;
|
|
6504
6684
|
if (process.platform === "win32") {
|
|
6505
6685
|
const systemRoot = process.env.SystemRoot ?? process.env.WINDIR ?? "C:\\Windows";
|
|
6506
|
-
const taskkill =
|
|
6686
|
+
const taskkill = path34.join(systemRoot, "System32", "taskkill.exe");
|
|
6507
6687
|
const result = spawnSync2(taskkill, ["/pid", String(pid), "/t", "/f"], {
|
|
6508
6688
|
shell: false,
|
|
6509
6689
|
stdio: "ignore",
|
|
@@ -6749,9 +6929,9 @@ async function runQualityPlan(input) {
|
|
|
6749
6929
|
}
|
|
6750
6930
|
|
|
6751
6931
|
// src/lib/engram-install.ts
|
|
6752
|
-
import
|
|
6932
|
+
import fs26 from "fs";
|
|
6753
6933
|
import os5 from "os";
|
|
6754
|
-
import
|
|
6934
|
+
import path35 from "path";
|
|
6755
6935
|
import { execFileSync as execFileSync7 } from "child_process";
|
|
6756
6936
|
import { createHash as createHash3 } from "crypto";
|
|
6757
6937
|
|
|
@@ -6796,15 +6976,15 @@ var engram_release_default = {
|
|
|
6796
6976
|
async function installMissingEngram(options = {}) {
|
|
6797
6977
|
const platform = options.platform ?? process.platform;
|
|
6798
6978
|
const homeDir = options.homeDir ?? os5.homedir();
|
|
6799
|
-
if (!
|
|
6979
|
+
if (!path35.isAbsolute(homeDir)) return { ok: false, reason: "El directorio personal debe ser absoluto." };
|
|
6800
6980
|
const binaryName = platform === "win32" ? "engram.exe" : "engram";
|
|
6801
|
-
const bin =
|
|
6981
|
+
const bin = path35.join(homeDir, ".local", "bin", binaryName);
|
|
6802
6982
|
let staging;
|
|
6803
6983
|
try {
|
|
6804
|
-
const existing =
|
|
6984
|
+
const existing = fs26.lstatSync(bin, { throwIfNoEntry: false });
|
|
6805
6985
|
if (existing) {
|
|
6806
|
-
if (!
|
|
6807
|
-
|
|
6986
|
+
if (!fs26.statSync(bin).isFile()) throw new Error(`El destino Engram no es un archivo: ${bin}`);
|
|
6987
|
+
fs26.accessSync(bin, platform === "win32" ? fs26.constants.F_OK : fs26.constants.X_OK);
|
|
6808
6988
|
return { ok: true, bin };
|
|
6809
6989
|
}
|
|
6810
6990
|
const key = `${platform === "win32" ? "windows" : platform}_${options.arch ?? process.arch}`;
|
|
@@ -6833,24 +7013,24 @@ async function installMissingEngram(options = {}) {
|
|
|
6833
7013
|
if (size !== asset.size || createHash3("sha256").update(archive).digest("hex") !== asset.sha256) {
|
|
6834
7014
|
throw new Error("Engram: tama\xF1o o hash SHA-256 no coincide con el artefacto aprobado.");
|
|
6835
7015
|
}
|
|
6836
|
-
|
|
6837
|
-
staging =
|
|
6838
|
-
const archivePath =
|
|
6839
|
-
|
|
7016
|
+
fs26.mkdirSync(path35.dirname(bin), { recursive: true });
|
|
7017
|
+
staging = fs26.mkdtempSync(path35.join(path35.dirname(bin), ".engram-install-"));
|
|
7018
|
+
const archivePath = path35.join(staging, asset.name);
|
|
7019
|
+
fs26.writeFileSync(archivePath, archive, { flag: "wx", mode: 384 });
|
|
6840
7020
|
const binary = execFileSync7(resolveTarBin(), ["-xOf", archivePath, binaryName], {
|
|
6841
7021
|
maxBuffer: 64 * 1024 * 1024,
|
|
6842
7022
|
timeout: 3e4,
|
|
6843
7023
|
stdio: ["ignore", "pipe", "pipe"]
|
|
6844
7024
|
});
|
|
6845
7025
|
if (binary.length === 0) throw new Error("El artefacto Engram no contiene un binario v\xE1lido.");
|
|
6846
|
-
const stagedBin =
|
|
6847
|
-
|
|
6848
|
-
|
|
7026
|
+
const stagedBin = path35.join(staging, binaryName);
|
|
7027
|
+
fs26.writeFileSync(stagedBin, binary, { flag: "wx", mode: 493 });
|
|
7028
|
+
fs26.linkSync(stagedBin, bin);
|
|
6849
7029
|
return { ok: true, bin };
|
|
6850
7030
|
} catch (error) {
|
|
6851
7031
|
return { ok: false, reason: error instanceof Error ? error.message : String(error) };
|
|
6852
7032
|
} finally {
|
|
6853
|
-
if (staging)
|
|
7033
|
+
if (staging) fs26.rmSync(staging, { recursive: true, force: true });
|
|
6854
7034
|
}
|
|
6855
7035
|
}
|
|
6856
7036
|
|
|
@@ -6877,6 +7057,9 @@ async function ensureOpenCodeModelsForInstall(command, flags, runtimes) {
|
|
|
6877
7057
|
);
|
|
6878
7058
|
return false;
|
|
6879
7059
|
}
|
|
7060
|
+
function shouldInspectPlaywrightCapability(targetDir, dryRun) {
|
|
7061
|
+
return targetDir === void 0 && !dryRun && browserPreferenceErrors().length === 0 && loadPlaywrightCliPreference() === true;
|
|
7062
|
+
}
|
|
6880
7063
|
function parseFlags(args, allowReceipt = false) {
|
|
6881
7064
|
const flags = {
|
|
6882
7065
|
agents: [],
|
|
@@ -7109,6 +7292,18 @@ async function resolveDevtoolsMcpSelection(command, flags, runtimes) {
|
|
|
7109
7292
|
const enabled = new Set(selected);
|
|
7110
7293
|
return Object.fromEntries(runtimes.map((runtime) => [runtime, enabled.has(runtime)]));
|
|
7111
7294
|
}
|
|
7295
|
+
function assertSelectedPromptFiles(runtimes, targetDir) {
|
|
7296
|
+
for (const runtime of runtimes) {
|
|
7297
|
+
if (runtime === "pi") assertSystemPromptFile(piSystemPromptFile(targetDir), targetDir);
|
|
7298
|
+
else {
|
|
7299
|
+
const adapter = ADAPTERS[runtime];
|
|
7300
|
+
if (!adapter) continue;
|
|
7301
|
+
const detection = adapter.detect();
|
|
7302
|
+
if (targetDir === void 0 && !detection.installed) continue;
|
|
7303
|
+
assertSystemPromptFile(adapter.paths(targetDir ?? detection.configDir).systemPromptFile, targetDir);
|
|
7304
|
+
}
|
|
7305
|
+
}
|
|
7306
|
+
}
|
|
7112
7307
|
function parseCliArgs(argv) {
|
|
7113
7308
|
const [first, ...rest] = argv;
|
|
7114
7309
|
const isCommand = COMMANDS.includes(first ?? "install");
|
|
@@ -7174,7 +7369,19 @@ async function resolveHostEngramForInstall(flags) {
|
|
|
7174
7369
|
return { ok: false, message: `Engram: ${error instanceof Error ? error.message : String(error)}` };
|
|
7175
7370
|
}
|
|
7176
7371
|
}
|
|
7177
|
-
async function runSelectedPi(
|
|
7372
|
+
async function runSelectedPi(options) {
|
|
7373
|
+
const {
|
|
7374
|
+
operation,
|
|
7375
|
+
targetDir,
|
|
7376
|
+
yes = false,
|
|
7377
|
+
resolvedEngramBin,
|
|
7378
|
+
devtoolsMcpEnabled,
|
|
7379
|
+
writingStyle: suppliedWritingStyle,
|
|
7380
|
+
modePreference,
|
|
7381
|
+
playwrightCliEnabled,
|
|
7382
|
+
playwrightCapability
|
|
7383
|
+
} = options;
|
|
7384
|
+
let writingStyle = suppliedWritingStyle;
|
|
7178
7385
|
if (targetDir === void 0 && operation !== "models") {
|
|
7179
7386
|
const preferenceErrors = browserPreferenceErrors();
|
|
7180
7387
|
if (preferenceErrors.length > 0) {
|
|
@@ -7220,6 +7427,7 @@ async function runSelectedPi(operation, targetDir, yes = false, resolvedEngramBi
|
|
|
7220
7427
|
engramBin = requirement.bin;
|
|
7221
7428
|
}
|
|
7222
7429
|
const result = await runManagedPiSystem({
|
|
7430
|
+
...options.packageOnly ? { packageOnly: true } : {},
|
|
7223
7431
|
writingStyle,
|
|
7224
7432
|
writingStyleMode: modePreference?.mode,
|
|
7225
7433
|
operation,
|
|
@@ -7227,7 +7435,8 @@ async function runSelectedPi(operation, targetDir, yes = false, resolvedEngramBi
|
|
|
7227
7435
|
detected: { executable: detected.executable, version: detected.version },
|
|
7228
7436
|
engramBin,
|
|
7229
7437
|
...devtoolsMcpEnabled === void 0 ? {} : { devtoolsMcpEnabled },
|
|
7230
|
-
...playwrightCliEnabled === void 0 ? {} : { playwrightCliEnabled }
|
|
7438
|
+
...playwrightCliEnabled === void 0 ? {} : { playwrightCliEnabled },
|
|
7439
|
+
...playwrightCapability === void 0 ? {} : { playwrightCapability }
|
|
7231
7440
|
});
|
|
7232
7441
|
if (result.kind === "blocked") {
|
|
7233
7442
|
const paths = "paths" in result ? `: ${result.paths.join(", ")}` : "";
|
|
@@ -7335,7 +7544,7 @@ Flags disponibles: jorgex-stack --help`
|
|
|
7335
7544
|
return;
|
|
7336
7545
|
}
|
|
7337
7546
|
try {
|
|
7338
|
-
const plan = JSON.parse(
|
|
7547
|
+
const plan = JSON.parse(fs27.readFileSync(flags.positional[0], "utf8"));
|
|
7339
7548
|
const result = await runQualityPlan(plan);
|
|
7340
7549
|
const serialized = serializeQualityReceipt(result.receipt);
|
|
7341
7550
|
if (flags.receipt === void 0) process.stdout.write(`${serialized}
|
|
@@ -7363,6 +7572,7 @@ Flags disponibles: jorgex-stack --help`
|
|
|
7363
7572
|
let completed = false;
|
|
7364
7573
|
p6.intro(`jorgex-stack ${command}${flags.dryRun ? " (dry-run)" : ""}`);
|
|
7365
7574
|
try {
|
|
7575
|
+
assertSelectedPromptFiles(runtimes, flags.targetDir);
|
|
7366
7576
|
const writingStyle = prepareWritingStyle(resolveWritingStyleFile({ targetDir: flags.targetDir }), { rootDir: flags.targetDir });
|
|
7367
7577
|
if (flags.targetDir === void 0) {
|
|
7368
7578
|
const errors = browserPreferenceErrors();
|
|
@@ -7384,6 +7594,11 @@ Flags disponibles: jorgex-stack --help`
|
|
|
7384
7594
|
exitCode = process.exitCode === 1 ? 1 : 0;
|
|
7385
7595
|
return;
|
|
7386
7596
|
}
|
|
7597
|
+
const playwrightToolPlan = resolvePlaywrightToolPlan(playwrightToolConsent);
|
|
7598
|
+
let playwrightCapability = shouldInspectPlaywrightCapability(flags.targetDir, flags.dryRun) && playwrightToolPlan.actions.length === 0 ? inspectPlaywrightCapability() : void 0;
|
|
7599
|
+
const capturePlaywrightCapability = (snapshot) => {
|
|
7600
|
+
playwrightCapability = snapshot;
|
|
7601
|
+
};
|
|
7387
7602
|
p6.log.info(`Estilo de escritura: ${writingStyle.sourcePath}${flags.dryRun ? " (instalaci\xF3n prevista; sin escrituras)" : ""}.`);
|
|
7388
7603
|
applyWritingStyle(writingStyle, flags.dryRun);
|
|
7389
7604
|
if (!await ensureOpenCodeModelsForInstall(command, flags, fileRuntimes)) {
|
|
@@ -7411,11 +7626,13 @@ Flags disponibles: jorgex-stack --help`
|
|
|
7411
7626
|
playwrightToolConsent,
|
|
7412
7627
|
devtoolsMcpSelection,
|
|
7413
7628
|
engramBin,
|
|
7629
|
+
...playwrightCapability === void 0 ? {} : { playwrightCapability },
|
|
7630
|
+
...playwrightToolPlan.actions.length === 0 ? {} : { onPlaywrightCapability: capturePlaywrightCapability },
|
|
7414
7631
|
showSummary: false
|
|
7415
7632
|
});
|
|
7416
7633
|
}
|
|
7417
7634
|
let piCanRun = true;
|
|
7418
|
-
if (command === "install" && fileRuntimes.length === 0 && runtimes.includes("pi") &&
|
|
7635
|
+
if (command === "install" && fileRuntimes.length === 0 && runtimes.includes("pi") && playwrightToolPlan.actions.length > 0) {
|
|
7419
7636
|
if (flags.dryRun) {
|
|
7420
7637
|
p6.log.info("Playwright CLI: instalaci\xF3n global y navegador previstos (dry-run; no se ejecutan).");
|
|
7421
7638
|
} else {
|
|
@@ -7427,6 +7644,7 @@ Flags disponibles: jorgex-stack --help`
|
|
|
7427
7644
|
yes: flags.yes,
|
|
7428
7645
|
playwrightToolConsent,
|
|
7429
7646
|
engramBin,
|
|
7647
|
+
onPlaywrightCapability: capturePlaywrightCapability,
|
|
7430
7648
|
showSummary: false
|
|
7431
7649
|
});
|
|
7432
7650
|
piCanRun = exitCode === 0;
|
|
@@ -7435,16 +7653,17 @@ Flags disponibles: jorgex-stack --help`
|
|
|
7435
7653
|
if (runtimes.includes("pi") && piCanRun) {
|
|
7436
7654
|
if (flags.dryRun) p6.log.info(`Pi: ${command} previsto; dry-run no ejecuta subprocess ni escribe receipt.`);
|
|
7437
7655
|
else {
|
|
7438
|
-
const piExitCode = await runSelectedPi(
|
|
7439
|
-
command,
|
|
7440
|
-
flags.targetDir,
|
|
7441
|
-
flags.yes,
|
|
7442
|
-
flags.targetDir === void 0 ? engramBin : void 0,
|
|
7443
|
-
devtoolsMcpSelection.pi,
|
|
7656
|
+
const piExitCode = await runSelectedPi({
|
|
7657
|
+
operation: command,
|
|
7658
|
+
targetDir: flags.targetDir,
|
|
7659
|
+
yes: flags.yes,
|
|
7660
|
+
resolvedEngramBin: flags.targetDir === void 0 ? engramBin : void 0,
|
|
7661
|
+
devtoolsMcpEnabled: devtoolsMcpSelection.pi,
|
|
7444
7662
|
writingStyle,
|
|
7445
|
-
mode,
|
|
7446
|
-
flags.targetDir === void 0 && exitCode === 0 &&
|
|
7447
|
-
|
|
7663
|
+
modePreference: mode,
|
|
7664
|
+
playwrightCliEnabled: flags.targetDir === void 0 && exitCode === 0 && playwrightToolPlan.actions.length > 0 ? playwrightToolConsent.runtimeSelection?.pi : void 0,
|
|
7665
|
+
playwrightCapability
|
|
7666
|
+
});
|
|
7448
7667
|
exitCode = Math.max(exitCode, piExitCode);
|
|
7449
7668
|
}
|
|
7450
7669
|
}
|
|
@@ -7468,6 +7687,13 @@ Flags disponibles: jorgex-stack --help`
|
|
|
7468
7687
|
process.exitCode = 1;
|
|
7469
7688
|
return;
|
|
7470
7689
|
}
|
|
7690
|
+
try {
|
|
7691
|
+
assertSelectedPromptFiles(runtimes, flags.targetDir);
|
|
7692
|
+
} catch (error) {
|
|
7693
|
+
p6.log.error(error instanceof Error ? error.message : String(error));
|
|
7694
|
+
process.exitCode = 1;
|
|
7695
|
+
return;
|
|
7696
|
+
}
|
|
7471
7697
|
const fileRuntimes = runtimes.filter(isFileManagedRuntime);
|
|
7472
7698
|
let exitCode = fileRuntimes.length > 0 || flags.removePlaywright ? await runUninstall({
|
|
7473
7699
|
runtimes: fileRuntimes,
|
|
@@ -7479,7 +7705,7 @@ Flags disponibles: jorgex-stack --help`
|
|
|
7479
7705
|
}) : 0;
|
|
7480
7706
|
if (runtimes.includes("pi")) {
|
|
7481
7707
|
if (flags.dryRun) p6.log.info("Pi: uninstall previsto; dry-run conserva paquete y receipt.");
|
|
7482
|
-
else exitCode = Math.max(exitCode, await runSelectedPi("uninstall", flags.targetDir));
|
|
7708
|
+
else exitCode = Math.max(exitCode, await runSelectedPi({ operation: "uninstall", targetDir: flags.targetDir }));
|
|
7483
7709
|
}
|
|
7484
7710
|
process.exitCode = exitCode;
|
|
7485
7711
|
return;
|
|
@@ -7492,8 +7718,25 @@ Flags disponibles: jorgex-stack --help`
|
|
|
7492
7718
|
...Object.values(ADAPTERS).filter((adapter) => adapter.detect().installed).map((adapter) => adapter.id),
|
|
7493
7719
|
...piSelected ? ["pi"] : []
|
|
7494
7720
|
] : [...Object.keys(ADAPTERS), ...piSelected ? ["pi"] : []];
|
|
7495
|
-
|
|
7496
|
-
|
|
7721
|
+
const doctorCapability = shouldInspectPlaywrightCapability(flags.targetDir, flags.dryRun) ? inspectPlaywrightCapability() : void 0;
|
|
7722
|
+
let exitCode = await runDoctor({
|
|
7723
|
+
targetDir: flags.targetDir,
|
|
7724
|
+
runtimes: doctorRuntimes,
|
|
7725
|
+
mode,
|
|
7726
|
+
...flags.dryRun ? { dryRun: true } : {},
|
|
7727
|
+
...doctorCapability === void 0 ? {} : { playwrightCapability: doctorCapability }
|
|
7728
|
+
});
|
|
7729
|
+
if (piSelected) {
|
|
7730
|
+
if (flags.dryRun) p6.log.info("Pi: doctor previsto; dry-run no ejecuta subprocess ni smoke de Playwright.");
|
|
7731
|
+
else {
|
|
7732
|
+
exitCode = Math.max(exitCode, await runSelectedPi({
|
|
7733
|
+
operation: "doctor",
|
|
7734
|
+
targetDir: flags.targetDir,
|
|
7735
|
+
modePreference: mode,
|
|
7736
|
+
playwrightCapability: doctorCapability
|
|
7737
|
+
}));
|
|
7738
|
+
}
|
|
7739
|
+
}
|
|
7497
7740
|
process.exitCode = exitCode;
|
|
7498
7741
|
return;
|
|
7499
7742
|
}
|
|
@@ -7502,12 +7745,28 @@ Flags disponibles: jorgex-stack --help`
|
|
|
7502
7745
|
const piExplicit = flags.agents.includes("pi");
|
|
7503
7746
|
const fileRuntimeExplicit = flags.agents.some(isFileManagedRuntime);
|
|
7504
7747
|
let exitCode = flags.agents.length === 0 || fileRuntimeExplicit ? await runUpdateCheck(VERSION, flags.targetDir === void 0) : 0;
|
|
7505
|
-
if (piExplicit)
|
|
7748
|
+
if (piExplicit) {
|
|
7749
|
+
if (flags.dryRun) p6.log.info("Pi: doctor previsto; dry-run no ejecuta subprocess ni smoke de Playwright.");
|
|
7750
|
+
else {
|
|
7751
|
+
exitCode = Math.max(exitCode, await runSelectedPi({
|
|
7752
|
+
operation: "doctor",
|
|
7753
|
+
targetDir: flags.targetDir,
|
|
7754
|
+
packageOnly: true
|
|
7755
|
+
}));
|
|
7756
|
+
}
|
|
7757
|
+
}
|
|
7506
7758
|
process.exitCode = exitCode;
|
|
7507
7759
|
return;
|
|
7508
7760
|
}
|
|
7509
7761
|
const runtimes = await resolveRuntimes(flags);
|
|
7510
7762
|
if (runtimes === null) return;
|
|
7763
|
+
try {
|
|
7764
|
+
assertSelectedPromptFiles(runtimes, flags.targetDir);
|
|
7765
|
+
} catch (error) {
|
|
7766
|
+
p6.log.error(error instanceof Error ? error.message : String(error));
|
|
7767
|
+
process.exitCode = 1;
|
|
7768
|
+
return;
|
|
7769
|
+
}
|
|
7511
7770
|
const fileRuntimes = runtimes.filter(isFileManagedRuntime);
|
|
7512
7771
|
const preferenceFile = installModePreferenceFile();
|
|
7513
7772
|
const explicitMode = flags.mode !== void 0 || flags.subagentConcurrency !== void 0;
|
|
@@ -7520,8 +7779,15 @@ Flags disponibles: jorgex-stack --help`
|
|
|
7520
7779
|
{ rootDir: flags.targetDir }
|
|
7521
7780
|
);
|
|
7522
7781
|
applyWritingStyle(writingStyle, flags.dryRun);
|
|
7782
|
+
let updateCapability = shouldInspectPlaywrightCapability(flags.targetDir, flags.dryRun) ? inspectPlaywrightCapability() : void 0;
|
|
7523
7783
|
if (fileRuntimes.length === 0 && runtimes.includes("pi")) {
|
|
7524
|
-
const piExitCode = await runSelectedPi(
|
|
7784
|
+
const piExitCode = await runSelectedPi({
|
|
7785
|
+
operation: "update",
|
|
7786
|
+
targetDir: flags.targetDir,
|
|
7787
|
+
writingStyle,
|
|
7788
|
+
modePreference: mode,
|
|
7789
|
+
playwrightCapability: updateCapability
|
|
7790
|
+
});
|
|
7525
7791
|
process.exitCode = piExitCode;
|
|
7526
7792
|
persistSuccessfulGlobalMode(mode, flags.targetDir, flags.dryRun, piExitCode);
|
|
7527
7793
|
return;
|
|
@@ -7534,7 +7800,8 @@ Flags disponibles: jorgex-stack --help`
|
|
|
7534
7800
|
targetDir: flags.targetDir,
|
|
7535
7801
|
dryRun: flags.dryRun,
|
|
7536
7802
|
yes: true,
|
|
7537
|
-
mode
|
|
7803
|
+
mode,
|
|
7804
|
+
...updateCapability === void 0 ? {} : { playwrightCapability: updateCapability }
|
|
7538
7805
|
});
|
|
7539
7806
|
if (code !== 0) {
|
|
7540
7807
|
process.exitCode = code;
|
|
@@ -7550,12 +7817,35 @@ Flags disponibles: jorgex-stack --help`
|
|
|
7550
7817
|
flags.targetDir === void 0
|
|
7551
7818
|
);
|
|
7552
7819
|
process.exitCode = result.exitCode;
|
|
7820
|
+
let playwrightReconciled = false;
|
|
7821
|
+
if (result.exitCode === 0 && result.playwrightCapability !== void 0) {
|
|
7822
|
+
updateCapability = result.playwrightCapability;
|
|
7823
|
+
if (fileRuntimes.length > 0 && canSync) {
|
|
7824
|
+
const code = await runInstall({
|
|
7825
|
+
runtimes: fileRuntimes,
|
|
7826
|
+
writingStyle,
|
|
7827
|
+
targetDir: flags.targetDir,
|
|
7828
|
+
dryRun: false,
|
|
7829
|
+
yes: true,
|
|
7830
|
+
mode,
|
|
7831
|
+
playwrightCapability: updateCapability
|
|
7832
|
+
});
|
|
7833
|
+
process.exitCode = Math.max(process.exitCode ?? 0, code);
|
|
7834
|
+
playwrightReconciled = true;
|
|
7835
|
+
}
|
|
7836
|
+
}
|
|
7553
7837
|
if (result.exitCode === 0 && runtimes.includes("pi")) {
|
|
7554
|
-
process.exitCode = Math.max(process.exitCode, await runSelectedPi(
|
|
7838
|
+
process.exitCode = Math.max(process.exitCode ?? 0, await runSelectedPi({
|
|
7839
|
+
operation: "update",
|
|
7840
|
+
targetDir: flags.targetDir,
|
|
7841
|
+
writingStyle,
|
|
7842
|
+
modePreference: mode,
|
|
7843
|
+
playwrightCapability: updateCapability
|
|
7844
|
+
}));
|
|
7555
7845
|
}
|
|
7556
7846
|
if (result.syncRequired && fileRuntimes.length > 0 && (result.exitCode !== 0 || !canSync)) {
|
|
7557
7847
|
p6.log.warn("Skills/stack actualizados, pero el sync con los runtimes sigue pendiente. Ejecuta jorgex-stack sync --mode human|programmatic.");
|
|
7558
|
-
} else if (result.exitCode === 0 && result.syncRequired && fileRuntimes.length > 0 && canSync && !flags.yes && process.stdout.isTTY) {
|
|
7848
|
+
} else if (!playwrightReconciled && result.exitCode === 0 && result.syncRequired && fileRuntimes.length > 0 && canSync && !flags.yes && process.stdout.isTTY) {
|
|
7559
7849
|
const apply = await p6.confirm({ message: "\xBFRe-aplicar a los runtimes ahora? (sync)" });
|
|
7560
7850
|
if (!p6.isCancel(apply) && apply) {
|
|
7561
7851
|
process.exitCode = await runInstall({
|
|
@@ -7564,7 +7854,8 @@ Flags disponibles: jorgex-stack --help`
|
|
|
7564
7854
|
targetDir: flags.targetDir,
|
|
7565
7855
|
dryRun: false,
|
|
7566
7856
|
yes: false,
|
|
7567
|
-
mode
|
|
7857
|
+
mode,
|
|
7858
|
+
...updateCapability === void 0 ? {} : { playwrightCapability: updateCapability }
|
|
7568
7859
|
});
|
|
7569
7860
|
} else {
|
|
7570
7861
|
console.log("Sin aplicar. Cuando quieras: jorgex-stack sync");
|
|
@@ -7587,7 +7878,7 @@ Flags disponibles: jorgex-stack --help`
|
|
|
7587
7878
|
}
|
|
7588
7879
|
const fileRuntimes = runtimes.filter(isFileManagedRuntime);
|
|
7589
7880
|
let code = fileRuntimes.length > 0 ? await runModelsPicker({ yes: flags.yes, runtimes: fileRuntimes }) : 0;
|
|
7590
|
-
if (runtimes.includes("pi")) code = Math.max(code, await runSelectedPi("models", flags.targetDir));
|
|
7881
|
+
if (runtimes.includes("pi")) code = Math.max(code, await runSelectedPi({ operation: "models", targetDir: flags.targetDir }));
|
|
7591
7882
|
process.exitCode = code;
|
|
7592
7883
|
if (code === 0 && fileRuntimes.length > 0 && !flags.yes && process.stdout.isTTY) {
|
|
7593
7884
|
const apply = await p6.confirm({ message: "\xBFAplicar ahora los modelos a los agentes instalados? (sync)" });
|
|
@@ -7602,7 +7893,15 @@ Flags disponibles: jorgex-stack --help`
|
|
|
7602
7893
|
}
|
|
7603
7894
|
const mode = await resolveInstallMode(flags, false);
|
|
7604
7895
|
if (mode === null) return;
|
|
7605
|
-
|
|
7896
|
+
const playwrightCapability = shouldInspectPlaywrightCapability(flags.targetDir, flags.dryRun) ? inspectPlaywrightCapability() : void 0;
|
|
7897
|
+
process.exitCode = await runInstall({
|
|
7898
|
+
runtimes: fileRuntimes,
|
|
7899
|
+
targetDir: flags.targetDir,
|
|
7900
|
+
dryRun: flags.dryRun,
|
|
7901
|
+
yes: false,
|
|
7902
|
+
mode,
|
|
7903
|
+
...playwrightCapability === void 0 ? {} : { playwrightCapability }
|
|
7904
|
+
});
|
|
7606
7905
|
} else {
|
|
7607
7906
|
console.log("Sin aplicar. Cuando quieras: jorgex-stack sync");
|
|
7608
7907
|
}
|