jorgex-stack 1.9.37 → 1.9.39
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 +3 -3
- package/dist/cli.js +579 -428
- 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
684
|
function lookPath(cmd) {
|
|
619
685
|
const exts = process.platform === "win32" ? [".exe", ".cmd", ".bat", ".ps1", ""] : [""];
|
|
620
|
-
for (const dir of (process.env.PATH ?? "").split(
|
|
686
|
+
for (const dir of (process.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",
|
|
@@ -2296,9 +2353,9 @@ function detectPlaywrightCli() {
|
|
|
2296
2353
|
if (binPath === null) {
|
|
2297
2354
|
const pnpmHome = resolvePnpmHome();
|
|
2298
2355
|
const name = process.platform === "win32" ? `${PLAYWRIGHT_CLI.bin}.cmd` : PLAYWRIGHT_CLI.bin;
|
|
2299
|
-
const known = [
|
|
2356
|
+
const known = [path20.join(pnpmHome, name), path20.join(pnpmHome, "bin", name)].find((file) => {
|
|
2300
2357
|
try {
|
|
2301
|
-
return
|
|
2358
|
+
return fs15.statSync(file).isFile();
|
|
2302
2359
|
} catch {
|
|
2303
2360
|
return false;
|
|
2304
2361
|
}
|
|
@@ -2317,7 +2374,7 @@ function resolvePnpmBin() {
|
|
|
2317
2374
|
return pnpmCmd !== null && !pnpmCmd.toLowerCase().endsWith(".ps1") ? pnpmCmd : null;
|
|
2318
2375
|
}
|
|
2319
2376
|
function resolvePnpmHome(env = process.env, platform = process.platform, homeDir = os.homedir()) {
|
|
2320
|
-
const paths = platform === "win32" ?
|
|
2377
|
+
const paths = platform === "win32" ? path20.win32 : path20.posix;
|
|
2321
2378
|
if (env.PNPM_HOME) return env.PNPM_HOME;
|
|
2322
2379
|
if (env.XDG_DATA_HOME) return paths.join(env.XDG_DATA_HOME, "pnpm");
|
|
2323
2380
|
if (platform === "darwin") return paths.join(homeDir, "Library", "pnpm");
|
|
@@ -2326,7 +2383,7 @@ function resolvePnpmHome(env = process.env, platform = process.platform, homeDir
|
|
|
2326
2383
|
}
|
|
2327
2384
|
function setupPnpmGlobal(pnpmBin, env = process.env, platform = process.platform, homeDir = os.homedir()) {
|
|
2328
2385
|
const pnpmHome = resolvePnpmHome(env, platform, homeDir);
|
|
2329
|
-
const paths = platform === "win32" ?
|
|
2386
|
+
const paths = platform === "win32" ? path20.win32 : path20.posix;
|
|
2330
2387
|
if (!paths.isAbsolute(pnpmHome)) return { ok: false, reason: "PNPM_HOME debe ser una ruta absoluta; corrige la configuraci\xF3n antes de reintentar." };
|
|
2331
2388
|
const pathEntries = [pnpmHome, paths.join(pnpmHome, "bin")];
|
|
2332
2389
|
if (env.PATH !== void 0) pathEntries.push(env.PATH);
|
|
@@ -2346,10 +2403,10 @@ function setupPnpmGlobal(pnpmBin, env = process.env, platform = process.platform
|
|
|
2346
2403
|
}
|
|
2347
2404
|
function isPlaywrightBrowserReady(env = process.env, platform = process.platform, homeDir = os.homedir()) {
|
|
2348
2405
|
const configuredPath = env.PLAYWRIGHT_BROWSERS_PATH;
|
|
2349
|
-
const cacheDir = configuredPath ?? (platform === "win32" ?
|
|
2406
|
+
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
2407
|
if (configuredPath === "0") return { status: "missing", path: cacheDir, errorCode: "DISABLED" };
|
|
2351
2408
|
try {
|
|
2352
|
-
const ready =
|
|
2409
|
+
const ready = fs15.readdirSync(cacheDir, { withFileTypes: true }).some(
|
|
2353
2410
|
(entry) => entry.isDirectory() && /^chromium(?:_headless_shell)?-/.test(entry.name)
|
|
2354
2411
|
);
|
|
2355
2412
|
return ready ? { status: "ready", path: cacheDir } : { status: "missing", path: cacheDir };
|
|
@@ -2381,9 +2438,9 @@ function verifyPlaywrightBrowser(pnpmBin, env = process.env, cwd) {
|
|
|
2381
2438
|
cwd,
|
|
2382
2439
|
stdio: ["ignore", "pipe", "inherit"]
|
|
2383
2440
|
}).trim();
|
|
2384
|
-
if (!
|
|
2385
|
-
const packageFile =
|
|
2386
|
-
const installed = JSON.parse(
|
|
2441
|
+
if (!path20.isAbsolute(root)) return false;
|
|
2442
|
+
const packageFile = fs15.realpathSync(path20.join(root, "@playwright", "cli", "package.json"));
|
|
2443
|
+
const installed = JSON.parse(fs15.readFileSync(packageFile, "utf8"));
|
|
2387
2444
|
if (installed.name !== PLAYWRIGHT_CLI.packageName || installed.version !== PLAYWRIGHT_CLI.version) return false;
|
|
2388
2445
|
const probe = `
|
|
2389
2446
|
const { createRequire } = require('node:module');
|
|
@@ -2426,7 +2483,7 @@ function executePlaywrightToolAction(action, pnpmBin = resolvePnpmBin(), env) {
|
|
|
2426
2483
|
const command = planPlaywrightCliCommand(action, pnpmBin);
|
|
2427
2484
|
const invocation = planDetectedBinCommand(command.command, command.args);
|
|
2428
2485
|
if (invocation === null) return { ok: false, reason: "pnpm-command" };
|
|
2429
|
-
const cwd = action === "install-browser" ?
|
|
2486
|
+
const cwd = action === "install-browser" ? fs15.mkdtempSync(path20.join(os.tmpdir(), "jorgex-playwright-")) : void 0;
|
|
2430
2487
|
const childEnv = action === "install-browser" ? { ...env ?? process.env, NO_UPDATE_NOTIFIER: "1" } : env ?? process.env;
|
|
2431
2488
|
try {
|
|
2432
2489
|
execFileSync2(invocation.command, invocation.args, {
|
|
@@ -2441,27 +2498,27 @@ function executePlaywrightToolAction(action, pnpmBin = resolvePnpmBin(), env) {
|
|
|
2441
2498
|
} catch {
|
|
2442
2499
|
return { ok: false, reason: "action-failed" };
|
|
2443
2500
|
} finally {
|
|
2444
|
-
if (cwd !== void 0)
|
|
2501
|
+
if (cwd !== void 0) fs15.rmSync(cwd, { recursive: true, force: true });
|
|
2445
2502
|
}
|
|
2446
2503
|
}
|
|
2447
2504
|
|
|
2448
2505
|
// src/lib/tool-preferences.ts
|
|
2449
|
-
import
|
|
2450
|
-
import
|
|
2506
|
+
import fs16 from "fs";
|
|
2507
|
+
import path21 from "path";
|
|
2451
2508
|
var PLAYWRIGHT_CLI_PREFERENCE_VERSION = 1;
|
|
2452
2509
|
var DEVTOOLS_MCP_PREFERENCE_VERSION = 1;
|
|
2453
2510
|
var PRIMARY_MODEL_OWNERSHIP_VERSION = 1;
|
|
2454
2511
|
var PLAYWRIGHT_RUNTIMES = ["opencode", "claude-code", "codex", "pi"];
|
|
2455
2512
|
function readPreference(file) {
|
|
2456
2513
|
try {
|
|
2457
|
-
return { raw:
|
|
2514
|
+
return { raw: fs16.readFileSync(file, "utf8"), errorCode: null };
|
|
2458
2515
|
} catch (error) {
|
|
2459
2516
|
const code = error instanceof Error && "code" in error && typeof error.code === "string" ? error.code : "UNKNOWN";
|
|
2460
2517
|
return code === "ENOENT" ? { raw: null, errorCode: null } : { raw: null, errorCode: code };
|
|
2461
2518
|
}
|
|
2462
2519
|
}
|
|
2463
2520
|
function playwrightCliPreferenceFile(stateDir = dataDir()) {
|
|
2464
|
-
return
|
|
2521
|
+
return path21.join(stateDir, "playwright-cli.json");
|
|
2465
2522
|
}
|
|
2466
2523
|
function parsePlaywrightCliPreference(raw) {
|
|
2467
2524
|
try {
|
|
@@ -2507,10 +2564,10 @@ function savePlaywrightCliPreference(file, enabled, selection) {
|
|
|
2507
2564
|
writeText(file, content);
|
|
2508
2565
|
}
|
|
2509
2566
|
function devtoolsMcpPreferenceFile(stateDir = dataDir()) {
|
|
2510
|
-
return
|
|
2567
|
+
return path21.join(stateDir, "devtools-mcp.json");
|
|
2511
2568
|
}
|
|
2512
2569
|
function primaryModelOwnershipFile(stateDir = dataDir()) {
|
|
2513
|
-
return
|
|
2570
|
+
return path21.join(stateDir, "primary-model.json");
|
|
2514
2571
|
}
|
|
2515
2572
|
function isRecord2(value) {
|
|
2516
2573
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
@@ -2620,7 +2677,7 @@ function primaryModelOwnershipError(file = primaryModelOwnershipFile()) {
|
|
|
2620
2677
|
return `Primary model: ownership inv\xE1lido en ${file}. Corrige o borra ese archivo antes de reintentar.`;
|
|
2621
2678
|
}
|
|
2622
2679
|
function primaryModelConfigKey(configDir) {
|
|
2623
|
-
const resolved =
|
|
2680
|
+
const resolved = path21.resolve(configDir);
|
|
2624
2681
|
return process.platform === "win32" ? resolved.toLowerCase() : resolved;
|
|
2625
2682
|
}
|
|
2626
2683
|
function loadPrimaryModelOwnership(file, runtime, configDir) {
|
|
@@ -2740,7 +2797,7 @@ function diffPlan(plan) {
|
|
|
2740
2797
|
if (current === null) return { action, status: "create" };
|
|
2741
2798
|
return { action, status: current === action.content ? "unchanged" : "update" };
|
|
2742
2799
|
}
|
|
2743
|
-
if (!
|
|
2800
|
+
if (!fs17.existsSync(action.target)) return { action, status: "create" };
|
|
2744
2801
|
return { action, status: sameFileContent(action.source, action.target) ? "unchanged" : "update" };
|
|
2745
2802
|
});
|
|
2746
2803
|
}
|
|
@@ -2766,7 +2823,7 @@ function collectAllCurrentTargets(mode = DEFAULT_INSTALL_MODE_PREFERENCE) {
|
|
|
2766
2823
|
continue;
|
|
2767
2824
|
}
|
|
2768
2825
|
try {
|
|
2769
|
-
for (const action of buildPlan(adapter, ctx)) targets.add(
|
|
2826
|
+
for (const action of buildPlan(adapter, ctx)) targets.add(path22.resolve(action.target));
|
|
2770
2827
|
} catch (error) {
|
|
2771
2828
|
complete = false;
|
|
2772
2829
|
warnings.push(
|
|
@@ -2779,6 +2836,18 @@ function collectAllCurrentTargets(mode = DEFAULT_INSTALL_MODE_PREFERENCE) {
|
|
|
2779
2836
|
async function runInstall(opts) {
|
|
2780
2837
|
const showSummary = opts.showSummary !== false;
|
|
2781
2838
|
if (showSummary) p.intro(`jorgex-stack ${opts.dryRun ? "install (dry-run)" : "install"}`);
|
|
2839
|
+
try {
|
|
2840
|
+
for (const id of opts.runtimes) {
|
|
2841
|
+
const adapter = ADAPTERS[id];
|
|
2842
|
+
if (!adapter) continue;
|
|
2843
|
+
const detection = adapter.detect();
|
|
2844
|
+
if (opts.targetDir === void 0 && !detection.installed) continue;
|
|
2845
|
+
assertSystemPromptFile(adapter.paths(opts.targetDir ?? detection.configDir).systemPromptFile, opts.targetDir);
|
|
2846
|
+
}
|
|
2847
|
+
} catch (error) {
|
|
2848
|
+
p.log.error(error instanceof Error ? error.message : String(error));
|
|
2849
|
+
return 1;
|
|
2850
|
+
}
|
|
2782
2851
|
let writingStyle;
|
|
2783
2852
|
let preparedStyle;
|
|
2784
2853
|
try {
|
|
@@ -2889,10 +2958,10 @@ async function runInstall(opts) {
|
|
|
2889
2958
|
}
|
|
2890
2959
|
const writeManifest = () => {
|
|
2891
2960
|
if (!useManifest) return;
|
|
2892
|
-
const unmergeTargets = new Set(adapter.planUnmerge(canonicalMcp, canonicalHooks, ctx).map((a) =>
|
|
2961
|
+
const unmergeTargets = new Set(adapter.planUnmerge(canonicalMcp, canonicalHooks, ctx).map((a) => path22.resolve(a.target)));
|
|
2893
2962
|
const keepTarget = (target) => !unmergeTargets.has(target);
|
|
2894
|
-
const liveOwned = plan.map((a) =>
|
|
2895
|
-
const previousOwned = (prevManifest?.owned ?? []).map((target) =>
|
|
2963
|
+
const liveOwned = plan.map((a) => path22.resolve(a.target)).filter(keepTarget);
|
|
2964
|
+
const previousOwned = (prevManifest?.owned ?? []).map((target) => path22.resolve(target)).filter(keepTarget);
|
|
2896
2965
|
const owned = canOrphan ? liveOwned : [.../* @__PURE__ */ new Set([...previousOwned, ...liveOwned])];
|
|
2897
2966
|
writeRuntimeManifest(id, { configDir, owned, updatedAt: (/* @__PURE__ */ new Date()).toISOString() });
|
|
2898
2967
|
};
|
|
@@ -2921,9 +2990,9 @@ async function runInstall(opts) {
|
|
|
2921
2990
|
const backup = useManifest ? createBackup([...updates.map((c) => c.action.target), ...orphans], `install-${id}`) : null;
|
|
2922
2991
|
if (backup) p.log.info(`Backup: ${backup.id} (${backup.files.length} archivos)`);
|
|
2923
2992
|
applyChanges(changes, useManifest ? (action) => persistConfigurationOwnershipChanges(id, configDir, [action]) : void 0);
|
|
2924
|
-
const pruneRoot = useManifest ? HOME :
|
|
2993
|
+
const pruneRoot = useManifest ? HOME : path22.dirname(configDir);
|
|
2925
2994
|
for (const orphan of orphans) {
|
|
2926
|
-
|
|
2995
|
+
fs17.rmSync(orphan, { force: true });
|
|
2927
2996
|
pruneEmptyDirs(orphan, pruneRoot);
|
|
2928
2997
|
}
|
|
2929
2998
|
const verifyCtx = { ...ctx, warnings: [] };
|
|
@@ -3048,29 +3117,33 @@ async function runInstall(opts) {
|
|
|
3048
3117
|
}
|
|
3049
3118
|
|
|
3050
3119
|
// src/uninstall.ts
|
|
3051
|
-
import
|
|
3052
|
-
import
|
|
3120
|
+
import fs19 from "fs";
|
|
3121
|
+
import path26 from "path";
|
|
3053
3122
|
import * as p2 from "@clack/prompts";
|
|
3054
3123
|
|
|
3055
3124
|
// src/lib/pi-projection-lifecycle.ts
|
|
3056
|
-
import
|
|
3057
|
-
import
|
|
3125
|
+
import fs18 from "fs";
|
|
3126
|
+
import path25 from "path";
|
|
3058
3127
|
import { createHash } from "crypto";
|
|
3059
3128
|
|
|
3060
3129
|
// src/adapters/pi.ts
|
|
3061
|
-
import
|
|
3130
|
+
import path23 from "path";
|
|
3131
|
+
function piSystemPromptFile(targetDir) {
|
|
3132
|
+
const configDir = targetDir === void 0 ? process.env.PI_CODING_AGENT_DIR ?? path23.join(HOME, ".pi", "agent") : path23.join(path23.resolve(targetDir), "pi-agent");
|
|
3133
|
+
return path23.join(configDir, "AGENTS.md");
|
|
3134
|
+
}
|
|
3062
3135
|
var piAdapter = {
|
|
3063
3136
|
id: "pi",
|
|
3064
3137
|
paths(configDir) {
|
|
3065
|
-
const piConfigDir =
|
|
3066
|
-
const agentsHome = samePath(configDir, piConfigDir) ? HOME :
|
|
3138
|
+
const piConfigDir = path23.dirname(piSystemPromptFile());
|
|
3139
|
+
const agentsHome = samePath(configDir, piConfigDir) ? HOME : path23.join(path23.dirname(configDir), "home");
|
|
3067
3140
|
return {
|
|
3068
|
-
systemPromptFile:
|
|
3069
|
-
agentsDir:
|
|
3070
|
-
skillsDir:
|
|
3071
|
-
commandsDir:
|
|
3141
|
+
systemPromptFile: path23.join(configDir, "AGENTS.md"),
|
|
3142
|
+
agentsDir: path23.join(configDir, "agents"),
|
|
3143
|
+
skillsDir: path23.join(agentsHome, ".agents", "skills"),
|
|
3144
|
+
commandsDir: path23.join(configDir, "prompts"),
|
|
3072
3145
|
pluginsDir: null,
|
|
3073
|
-
scriptsDir:
|
|
3146
|
+
scriptsDir: path23.join(configDir, "scripts"),
|
|
3074
3147
|
outputStylesDir: null,
|
|
3075
3148
|
profilesDir: null
|
|
3076
3149
|
};
|
|
@@ -3080,11 +3153,20 @@ var piAdapter = {
|
|
|
3080
3153
|
},
|
|
3081
3154
|
injectEngramProtocol() {
|
|
3082
3155
|
return true;
|
|
3156
|
+
},
|
|
3157
|
+
// Pi 0.8.18 recompone estos marcadores desde su snapshot: no conoce los nuevos.
|
|
3158
|
+
adaptSystemPromptSections(sections) {
|
|
3159
|
+
const { context7, playwright, "chrome-devtools": devtools, ...legacy } = sections;
|
|
3160
|
+
return {
|
|
3161
|
+
...legacy,
|
|
3162
|
+
"system-prompt": [legacy["system-prompt"], context7].filter(Boolean).join("\n\n"),
|
|
3163
|
+
browser: [playwright, devtools].filter(Boolean).join("\n\n")
|
|
3164
|
+
};
|
|
3083
3165
|
}
|
|
3084
3166
|
};
|
|
3085
3167
|
|
|
3086
3168
|
// src/lib/pi-package-lifecycle.ts
|
|
3087
|
-
import
|
|
3169
|
+
import path24 from "path";
|
|
3088
3170
|
var REQUIRED_CAPABILITIES = /* @__PURE__ */ new Set([
|
|
3089
3171
|
"foundation-contract-v1",
|
|
3090
3172
|
"runner-json-v1",
|
|
@@ -3216,7 +3298,7 @@ function parseReceiptShape(receiptJson) {
|
|
|
3216
3298
|
return null;
|
|
3217
3299
|
}
|
|
3218
3300
|
if (engram === void 0) return "upgrade-required";
|
|
3219
|
-
if (engram === null || typeof engram !== "object" || Array.isArray(engram) || typeof Reflect.get(engram, "binary") !== "string" || !
|
|
3301
|
+
if (engram === null || typeof engram !== "object" || Array.isArray(engram) || typeof Reflect.get(engram, "binary") !== "string" || !path24.isAbsolute(Reflect.get(engram, "binary"))) {
|
|
3220
3302
|
return null;
|
|
3221
3303
|
}
|
|
3222
3304
|
return parsed;
|
|
@@ -3253,7 +3335,7 @@ function planPiPackageLifecycle(input) {
|
|
|
3253
3335
|
if (input.receiptJson !== null) {
|
|
3254
3336
|
const parsedReceipt = parseReceipt(input.receiptJson, input.candidate, {
|
|
3255
3337
|
kind: input.scope.kind,
|
|
3256
|
-
codingAgentDir:
|
|
3338
|
+
codingAgentDir: path24.resolve(input.scope.codingAgentDir)
|
|
3257
3339
|
}, input.engramBin);
|
|
3258
3340
|
if (parsedReceipt === "upgrade-required") return blocked(input, "receipt-upgrade-required");
|
|
3259
3341
|
if (parsedReceipt === null) return blocked(input, "receipt-corrupt");
|
|
@@ -3288,7 +3370,7 @@ function planPiPackageLifecycle(input) {
|
|
|
3288
3370
|
},
|
|
3289
3371
|
receipt: expectedReceipt(input.candidate, "installing", {
|
|
3290
3372
|
kind: input.scope.kind,
|
|
3291
|
-
codingAgentDir:
|
|
3373
|
+
codingAgentDir: path24.resolve(input.scope.codingAgentDir)
|
|
3292
3374
|
}, input.engramBin),
|
|
3293
3375
|
ownership: ownership(true)
|
|
3294
3376
|
};
|
|
@@ -3303,7 +3385,7 @@ function parseRunnerRecord(stdout, stderr, command, candidate, packageRunner) {
|
|
|
3303
3385
|
const parsed = JSON.parse(body);
|
|
3304
3386
|
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) return null;
|
|
3305
3387
|
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" || !
|
|
3388
|
+
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
3389
|
return null;
|
|
3308
3390
|
}
|
|
3309
3391
|
return record;
|
|
@@ -3389,10 +3471,10 @@ function validateOwnedOperationState(input) {
|
|
|
3389
3471
|
}))) {
|
|
3390
3472
|
return { kind: "blocked", reason: "receipt-untrusted" };
|
|
3391
3473
|
}
|
|
3392
|
-
if (receipt.scope.kind !== (input.paths.targetDir ? "target-dir" : "real") ||
|
|
3474
|
+
if (receipt.scope.kind !== (input.paths.targetDir ? "target-dir" : "real") || path24.resolve(receipt.scope.codingAgentDir) !== path24.resolve(input.paths.codingAgentDir)) {
|
|
3393
3475
|
return { kind: "blocked", reason: "source-divergent" };
|
|
3394
3476
|
}
|
|
3395
|
-
if (input.engramBin !== null &&
|
|
3477
|
+
if (input.engramBin !== null && path24.resolve(receipt.engram.binary) !== path24.resolve(input.engramBin)) {
|
|
3396
3478
|
return { kind: "blocked", reason: "receipt-corrupt" };
|
|
3397
3479
|
}
|
|
3398
3480
|
const source = receipt.candidate.package.source;
|
|
@@ -3499,7 +3581,7 @@ var RETIRED_PLAYWRIGHT_FILES = [
|
|
|
3499
3581
|
"references/video-recording.md"
|
|
3500
3582
|
];
|
|
3501
3583
|
function retiredPlaywrightFiles(scope) {
|
|
3502
|
-
return RETIRED_PLAYWRIGHT_FILES.map((file) =>
|
|
3584
|
+
return RETIRED_PLAYWRIGHT_FILES.map((file) => path25.resolve(scope.home, ".agents", "skills", "playwright-cli", file));
|
|
3503
3585
|
}
|
|
3504
3586
|
function handoffEnabled(input, kind) {
|
|
3505
3587
|
return (kind === "devtools" ? input.devtoolsMcpEnabled : input.playwrightHandoffEnabled) === true;
|
|
@@ -3508,18 +3590,18 @@ var preparedPiProjectionUninstalls = /* @__PURE__ */ new WeakMap();
|
|
|
3508
3590
|
function projectionScope(scope) {
|
|
3509
3591
|
return {
|
|
3510
3592
|
...scope,
|
|
3511
|
-
settingsFile:
|
|
3593
|
+
settingsFile: path25.join(scope.codingAgentDir, "settings.json")
|
|
3512
3594
|
};
|
|
3513
3595
|
}
|
|
3514
3596
|
function isInside(root, file) {
|
|
3515
|
-
const relative =
|
|
3516
|
-
return relative === "" || relative !== ".." && !relative.startsWith(`..${
|
|
3597
|
+
const relative = path25.relative(path25.resolve(root), path25.resolve(file));
|
|
3598
|
+
return relative === "" || relative !== ".." && !relative.startsWith(`..${path25.sep}`) && !path25.isAbsolute(relative);
|
|
3517
3599
|
}
|
|
3518
3600
|
function isManagedPath(scope, file) {
|
|
3519
3601
|
return isInside(scope.home, file) || isInside(scope.codingAgentDir, file);
|
|
3520
3602
|
}
|
|
3521
3603
|
function isAllowedPath(scope, file) {
|
|
3522
|
-
return isManagedPath(scope, file) ||
|
|
3604
|
+
return isManagedPath(scope, file) || path25.resolve(file) === path25.resolve(scope.receiptFile);
|
|
3523
3605
|
}
|
|
3524
3606
|
function projectedPiAdapter(scope) {
|
|
3525
3607
|
const paths = piAdapter.paths(scope.codingAgentDir);
|
|
@@ -3527,11 +3609,11 @@ function projectedPiAdapter(scope) {
|
|
|
3527
3609
|
...piAdapter,
|
|
3528
3610
|
paths: () => ({
|
|
3529
3611
|
...paths,
|
|
3530
|
-
skillsDir:
|
|
3612
|
+
skillsDir: path25.join(scope.home, ".agents", "skills")
|
|
3531
3613
|
})
|
|
3532
3614
|
};
|
|
3533
3615
|
}
|
|
3534
|
-
function projectionPlan(input, scope) {
|
|
3616
|
+
function projectionPlan(input, scope, includePrompt = true) {
|
|
3535
3617
|
const ctx = {
|
|
3536
3618
|
writingStyle: input.writingStyle,
|
|
3537
3619
|
stackDir: input.stackDir,
|
|
@@ -3544,7 +3626,7 @@ function projectionPlan(input, scope) {
|
|
|
3544
3626
|
};
|
|
3545
3627
|
const adapter = projectedPiAdapter(scope);
|
|
3546
3628
|
const actions = [
|
|
3547
|
-
...planSystemPrompt(adapter, ctx),
|
|
3629
|
+
...includePrompt ? planSystemPrompt(adapter, ctx) : [],
|
|
3548
3630
|
...planSkills(adapter, ctx),
|
|
3549
3631
|
...planCommands(adapter, ctx)
|
|
3550
3632
|
];
|
|
@@ -3571,20 +3653,20 @@ function projectionPlan(input, scope) {
|
|
|
3571
3653
|
return actions;
|
|
3572
3654
|
}
|
|
3573
3655
|
function handoffPath(scope, kind) {
|
|
3574
|
-
return
|
|
3656
|
+
return path25.resolve(scope.codingAgentDir, "jorgex-pi", `${kind}.v1.json`);
|
|
3575
3657
|
}
|
|
3576
3658
|
function contentHash(content) {
|
|
3577
3659
|
return createHash("sha256").update(content).digest("hex");
|
|
3578
3660
|
}
|
|
3579
3661
|
function canonicalActionContent(action) {
|
|
3580
|
-
return action.kind === "write" ? action.content :
|
|
3662
|
+
return action.kind === "write" ? action.content : fs18.readFileSync(action.source, "utf8");
|
|
3581
3663
|
}
|
|
3582
3664
|
function hasActionDrift(action, deps) {
|
|
3583
3665
|
return deps.readText(action.target) !== canonicalActionContent(action);
|
|
3584
3666
|
}
|
|
3585
3667
|
function applyActions(actions, deps, handoffs = []) {
|
|
3586
3668
|
for (const action of actions) {
|
|
3587
|
-
const handoff = handoffs.find((entry) =>
|
|
3669
|
+
const handoff = handoffs.find((entry) => path25.resolve(action.target) === entry.file);
|
|
3588
3670
|
if (handoff) {
|
|
3589
3671
|
try {
|
|
3590
3672
|
if (deps.readText(handoff.file) !== handoff.previous) return handoffConflict(handoff.kind, handoff.file);
|
|
@@ -3604,9 +3686,9 @@ function assertPlanContained(plan, scope) {
|
|
|
3604
3686
|
}
|
|
3605
3687
|
}
|
|
3606
3688
|
function receiptFor(plan, scope) {
|
|
3607
|
-
const systemPrompt =
|
|
3689
|
+
const systemPrompt = path25.join(scope.codingAgentDir, "AGENTS.md");
|
|
3608
3690
|
const owned = [...new Set(
|
|
3609
|
-
plan.map((action) =>
|
|
3691
|
+
plan.map((action) => path25.resolve(action.target)).filter((target) => target !== systemPrompt)
|
|
3610
3692
|
)];
|
|
3611
3693
|
const receipt = {
|
|
3612
3694
|
schemaVersion: 1,
|
|
@@ -3619,7 +3701,7 @@ function receiptFor(plan, scope) {
|
|
|
3619
3701
|
owned
|
|
3620
3702
|
};
|
|
3621
3703
|
for (const kind of HANDOFF_KINDS) {
|
|
3622
|
-
const action = plan.find((entry) =>
|
|
3704
|
+
const action = plan.find((entry) => path25.resolve(entry.target) === handoffPath(scope, kind));
|
|
3623
3705
|
if (action) receipt[kind] = { sha256: contentHash(canonicalActionContent(action)) };
|
|
3624
3706
|
}
|
|
3625
3707
|
return receipt;
|
|
@@ -3660,7 +3742,7 @@ function parseReceipt2(raw, expected) {
|
|
|
3660
3742
|
if (matches(expectedOwned)) return { schemaVersion: 1, scope: expected.scope, owned: expectedOwned, ...digests };
|
|
3661
3743
|
const retired = retiredPlaywrightFiles(expected.scope);
|
|
3662
3744
|
if (retired.some((file) => expectedOwned.includes(file))) return null;
|
|
3663
|
-
const skillsRoot = `${
|
|
3745
|
+
const skillsRoot = `${path25.resolve(expected.scope.home, ".agents", "skills")}${path25.sep}`;
|
|
3664
3746
|
const insertion = expectedOwned.findIndex((file) => !file.startsWith(skillsRoot) || file > retired[0]);
|
|
3665
3747
|
const legacyOwned = [...expectedOwned];
|
|
3666
3748
|
legacyOwned.splice(insertion === -1 ? legacyOwned.length : insertion, 0, ...retired);
|
|
@@ -3671,7 +3753,7 @@ function parseReceipt2(raw, expected) {
|
|
|
3671
3753
|
}
|
|
3672
3754
|
}
|
|
3673
3755
|
function expectedProjectionReceipt(input, scope) {
|
|
3674
|
-
const plan = projectionPlan(input, scope);
|
|
3756
|
+
const plan = projectionPlan(input, scope, false);
|
|
3675
3757
|
assertPlanContained(plan, scope);
|
|
3676
3758
|
return receiptFor(plan, scope);
|
|
3677
3759
|
}
|
|
@@ -3679,13 +3761,13 @@ function realPiProjectionScope() {
|
|
|
3679
3761
|
return {
|
|
3680
3762
|
kind: "real",
|
|
3681
3763
|
home: HOME,
|
|
3682
|
-
codingAgentDir:
|
|
3683
|
-
receiptFile:
|
|
3764
|
+
codingAgentDir: path25.dirname(piSystemPromptFile()),
|
|
3765
|
+
receiptFile: path25.join(dataDir(), "pi-projection-receipt.json")
|
|
3684
3766
|
};
|
|
3685
3767
|
}
|
|
3686
3768
|
function readTextOnlyIfMissing(file) {
|
|
3687
3769
|
try {
|
|
3688
|
-
return
|
|
3770
|
+
return fs18.readFileSync(file, "utf8");
|
|
3689
3771
|
} catch (error) {
|
|
3690
3772
|
if (typeof error === "object" && error !== null && Reflect.get(error, "code") === "ENOENT") return null;
|
|
3691
3773
|
throw error;
|
|
@@ -3718,13 +3800,13 @@ function manifestOwned(manifest) {
|
|
|
3718
3800
|
return new Set([
|
|
3719
3801
|
...manifest.runtimes.codex?.owned ?? [],
|
|
3720
3802
|
...manifest.runtimes.opencode?.owned ?? []
|
|
3721
|
-
].map((file) =>
|
|
3803
|
+
].map((file) => path25.resolve(file)));
|
|
3722
3804
|
}
|
|
3723
3805
|
function withoutManagedPromptSections(content) {
|
|
3724
|
-
return
|
|
3806
|
+
return removeSystemPromptSections(content);
|
|
3725
3807
|
}
|
|
3726
3808
|
function uniquePaths(paths) {
|
|
3727
|
-
return [...new Set(paths.map((file) =>
|
|
3809
|
+
return [...new Set(paths.map((file) => path25.resolve(file)))];
|
|
3728
3810
|
}
|
|
3729
3811
|
function blocked2(reason, paths, remedy) {
|
|
3730
3812
|
return { kind: "blocked", reason, paths: uniquePaths(paths), remedy };
|
|
@@ -3831,15 +3913,20 @@ function preparePiProjectionUninstall(input, deps) {
|
|
|
3831
3913
|
}
|
|
3832
3914
|
const scope = projectionScope(input.scope);
|
|
3833
3915
|
const expectedReceipt2 = expectedProjectionReceipt(input, scope);
|
|
3834
|
-
const prompt =
|
|
3916
|
+
const prompt = path25.join(scope.codingAgentDir, "AGENTS.md");
|
|
3835
3917
|
let existingPrompt;
|
|
3836
3918
|
try {
|
|
3837
3919
|
existingPrompt = deps.readText(prompt);
|
|
3838
3920
|
} catch {
|
|
3839
3921
|
return cleanupFailure([prompt]);
|
|
3840
3922
|
}
|
|
3923
|
+
try {
|
|
3924
|
+
assertSystemPromptMarkers(existingPrompt, prompt);
|
|
3925
|
+
} catch (error) {
|
|
3926
|
+
return blocked2("projection-prompt-markers", [prompt], error instanceof Error ? error.message : String(error));
|
|
3927
|
+
}
|
|
3841
3928
|
const promptContent = existingPrompt === null ? null : withoutManagedPromptSections(existingPrompt);
|
|
3842
|
-
const promptUpdate = promptContent === null || promptContent === existingPrompt ? null : { file:
|
|
3929
|
+
const promptUpdate = promptContent === null || promptContent === existingPrompt ? null : { file: path25.resolve(prompt), content: promptContent };
|
|
3843
3930
|
const receiptRead = readProjectionReceipt(scope.receiptFile, deps);
|
|
3844
3931
|
if (receiptRead.kind === "unreadable") return receiptUnreadable(scope.receiptFile);
|
|
3845
3932
|
let ownedToRemove = [];
|
|
@@ -3857,7 +3944,7 @@ function preparePiProjectionUninstall(input, deps) {
|
|
|
3857
3944
|
return cleanupFailure([scope.receiptFile]);
|
|
3858
3945
|
}
|
|
3859
3946
|
ownedToRemove = receipt.owned.filter((owned) => !retained.has(owned));
|
|
3860
|
-
receiptFile =
|
|
3947
|
+
receiptFile = path25.resolve(scope.receiptFile);
|
|
3861
3948
|
backupTargets = [...backupTargets, ...receipt.owned, receiptFile];
|
|
3862
3949
|
}
|
|
3863
3950
|
const handoffs = [];
|
|
@@ -3935,10 +4022,16 @@ function runPiProjectionLifecycle(input, deps) {
|
|
|
3935
4022
|
const prepared = preparePiProjectionUninstall(input, deps);
|
|
3936
4023
|
return prepared.kind === "blocked" ? prepared : completePiProjectionUninstall(prepared.plan, deps);
|
|
3937
4024
|
}
|
|
3938
|
-
|
|
4025
|
+
const prompt = path25.join(scope.codingAgentDir, "AGENTS.md");
|
|
4026
|
+
try {
|
|
4027
|
+
assertSystemPromptMarkers(deps.readText(prompt), prompt);
|
|
4028
|
+
} catch (error) {
|
|
4029
|
+
return blocked2("projection-prompt-markers", [prompt], error instanceof Error ? error.message : String(error));
|
|
4030
|
+
}
|
|
4031
|
+
if (input.devtoolsMcpEnabled && (!input.pnpmBin || !path25.isAbsolute(input.pnpmBin))) {
|
|
3939
4032
|
return blocked2("projection-devtools-command", [handoffPath(scope, "devtools")], "DevTools requiere un ejecutable pnpm absoluto disponible en PATH.");
|
|
3940
4033
|
}
|
|
3941
|
-
if (input.playwrightHandoffEnabled && (!input.playwrightCliCommand || !
|
|
4034
|
+
if (input.playwrightHandoffEnabled && (!input.playwrightCliCommand || !path25.isAbsolute(input.playwrightCliCommand) || /[\u0000-\u001f\u007f]/.test(input.playwrightCliCommand))) {
|
|
3942
4035
|
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
4036
|
}
|
|
3944
4037
|
const plan = projectionPlan(input, scope);
|
|
@@ -3951,12 +4044,12 @@ function runPiProjectionLifecycle(input, deps) {
|
|
|
3951
4044
|
const expectedReceipt2 = receiptContent(receipt);
|
|
3952
4045
|
const drifted = plan.filter((action) => hasActionDrift(action, deps));
|
|
3953
4046
|
for (const { kind, file } of checked.handoffs) {
|
|
3954
|
-
if (handoffEnabled(input, kind) && checked.previous?.[kind]?.sha256 !== receipt[kind]?.sha256 && !drifted.some((action) =>
|
|
4047
|
+
if (handoffEnabled(input, kind) && checked.previous?.[kind]?.sha256 !== receipt[kind]?.sha256 && !drifted.some((action) => path25.resolve(action.target) === file)) {
|
|
3955
4048
|
return input.operation === "doctor" ? { kind: "drift", paths: [file] } : handoffConflict(kind, file);
|
|
3956
4049
|
}
|
|
3957
4050
|
}
|
|
3958
4051
|
if (input.operation === "doctor") {
|
|
3959
|
-
const paths = drifted.map((action) =>
|
|
4052
|
+
const paths = drifted.map((action) => path25.resolve(action.target));
|
|
3960
4053
|
paths.push(...removedHandoffs.map(({ file }) => file), ...retiredSkills);
|
|
3961
4054
|
const currentSettings2 = deps.readText(scope.settingsFile);
|
|
3962
4055
|
if (currentSettings2 === null || filterProjectedPiPackage(currentSettings2, input.packageSource) !== currentSettings2) {
|
|
@@ -4015,12 +4108,12 @@ function runPiProjectionLifecycle(input, deps) {
|
|
|
4015
4108
|
return { kind: "synced", changed: drifted.length > 0 || packageWillChange || receiptChanged || removedHandoffs.length > 0 };
|
|
4016
4109
|
}
|
|
4017
4110
|
function systemProjectionLifecycle(input) {
|
|
4018
|
-
const targetRoot = input.targetDir === void 0 ? null :
|
|
4111
|
+
const targetRoot = input.targetDir === void 0 ? null : path25.resolve(input.targetDir);
|
|
4019
4112
|
const scope = targetRoot === null ? realPiProjectionScope() : {
|
|
4020
4113
|
kind: "target-dir",
|
|
4021
|
-
home:
|
|
4022
|
-
codingAgentDir:
|
|
4023
|
-
receiptFile:
|
|
4114
|
+
home: path25.join(targetRoot, "home"),
|
|
4115
|
+
codingAgentDir: path25.dirname(piSystemPromptFile(targetRoot)),
|
|
4116
|
+
receiptFile: path25.join(targetRoot, "state", "pi-projection-receipt.json")
|
|
4024
4117
|
};
|
|
4025
4118
|
return {
|
|
4026
4119
|
input: {
|
|
@@ -4041,14 +4134,14 @@ function systemProjectionLifecycle(input) {
|
|
|
4041
4134
|
backup: (paths) => createBackup(
|
|
4042
4135
|
paths,
|
|
4043
4136
|
`pi-projection-${input.operation}`,
|
|
4044
|
-
targetRoot === null ? void 0 :
|
|
4137
|
+
targetRoot === null ? void 0 : path25.join(targetRoot, "backups")
|
|
4045
4138
|
),
|
|
4046
4139
|
writeText,
|
|
4047
4140
|
copyFile,
|
|
4048
4141
|
removeFile: (file) => {
|
|
4049
|
-
|
|
4050
|
-
if (retiredPlaywrightFiles(scope).includes(
|
|
4051
|
-
pruneEmptyDirs(file,
|
|
4142
|
+
fs18.rmSync(file, { force: true });
|
|
4143
|
+
if (retiredPlaywrightFiles(scope).includes(path25.resolve(file))) {
|
|
4144
|
+
pruneEmptyDirs(file, path25.join(scope.home, ".agents", "skills"));
|
|
4052
4145
|
}
|
|
4053
4146
|
},
|
|
4054
4147
|
readManifest: targetRoot === null ? readManifest : () => ({ runtimes: {} })
|
|
@@ -4056,6 +4149,8 @@ function systemProjectionLifecycle(input) {
|
|
|
4056
4149
|
};
|
|
4057
4150
|
}
|
|
4058
4151
|
function preparePiProjectionUninstallSystem(input) {
|
|
4152
|
+
const error = systemPromptPreflight(input.targetDir);
|
|
4153
|
+
if (error) return error;
|
|
4059
4154
|
const lifecycle = systemProjectionLifecycle(input);
|
|
4060
4155
|
return preparePiProjectionUninstall(lifecycle.input, lifecycle.deps);
|
|
4061
4156
|
}
|
|
@@ -4064,9 +4159,19 @@ function completePiProjectionUninstallSystem(plan, input) {
|
|
|
4064
4159
|
return completePiProjectionUninstall(plan, lifecycle.deps);
|
|
4065
4160
|
}
|
|
4066
4161
|
function runPiProjectionLifecycleSystem(input) {
|
|
4162
|
+
const error = systemPromptPreflight(input.targetDir);
|
|
4163
|
+
if (error) return error;
|
|
4067
4164
|
const lifecycle = systemProjectionLifecycle(input);
|
|
4068
4165
|
return runPiProjectionLifecycle(lifecycle.input, lifecycle.deps);
|
|
4069
4166
|
}
|
|
4167
|
+
function systemPromptPreflight(targetDir) {
|
|
4168
|
+
const target = piSystemPromptFile(targetDir);
|
|
4169
|
+
try {
|
|
4170
|
+
assertSystemPromptFile(target, targetDir);
|
|
4171
|
+
} catch (error) {
|
|
4172
|
+
return blocked2("projection-prompt-markers", [target], error instanceof Error ? error.message : String(error));
|
|
4173
|
+
}
|
|
4174
|
+
}
|
|
4070
4175
|
|
|
4071
4176
|
// src/uninstall.ts
|
|
4072
4177
|
function resolvePlaywrightUninstallPlan(input) {
|
|
@@ -4077,6 +4182,18 @@ function resolvePlaywrightUninstallPlan(input) {
|
|
|
4077
4182
|
}
|
|
4078
4183
|
async function runUninstall(opts) {
|
|
4079
4184
|
p2.intro(`jorgex-stack ${opts.dryRun ? "uninstall (dry-run)" : "uninstall"}`);
|
|
4185
|
+
try {
|
|
4186
|
+
for (const id of opts.runtimes) {
|
|
4187
|
+
const adapter = ADAPTERS[id];
|
|
4188
|
+
if (!adapter) continue;
|
|
4189
|
+
const detection = adapter.detect();
|
|
4190
|
+
if (opts.targetDir === void 0 && !detection.installed) continue;
|
|
4191
|
+
assertSystemPromptFile(adapter.paths(opts.targetDir ?? detection.configDir).systemPromptFile, opts.targetDir);
|
|
4192
|
+
}
|
|
4193
|
+
} catch (error) {
|
|
4194
|
+
p2.log.error(error instanceof Error ? error.message : String(error));
|
|
4195
|
+
return 1;
|
|
4196
|
+
}
|
|
4080
4197
|
const useBrowserPreferences = opts.targetDir === void 0;
|
|
4081
4198
|
const preferenceErrors = useBrowserPreferences ? [...browserPreferenceErrors(), primaryModelOwnershipError()].filter((error) => error !== null) : [];
|
|
4082
4199
|
if (preferenceErrors.length > 0) {
|
|
@@ -4127,7 +4244,7 @@ async function runUninstall(opts) {
|
|
|
4127
4244
|
if (!detection.installed) continue;
|
|
4128
4245
|
const keepCtx = makeContext(keep, detection.configDir);
|
|
4129
4246
|
if (!keepCtx) continue;
|
|
4130
|
-
for (const action of buildPlan(keep, keepCtx)) retained.add(
|
|
4247
|
+
for (const action of buildPlan(keep, keepCtx)) retained.add(path26.resolve(action.target));
|
|
4131
4248
|
}
|
|
4132
4249
|
}
|
|
4133
4250
|
for (const id of opts.runtimes) {
|
|
@@ -4146,15 +4263,15 @@ async function runUninstall(opts) {
|
|
|
4146
4263
|
if (!ctx) continue;
|
|
4147
4264
|
ctx.preserveEngram = !removeEngram;
|
|
4148
4265
|
const unmerge = adapter.planUnmerge(mcpForUnmerge, hooks, ctx);
|
|
4149
|
-
const mergedTargets = new Set(unmerge.map((a) =>
|
|
4266
|
+
const mergedTargets = new Set(unmerge.map((a) => path26.resolve(a.target)));
|
|
4150
4267
|
const usingRealConfig = opts.targetDir === void 0;
|
|
4151
4268
|
const prevOwned = usingRealConfig ? readManifest().runtimes[id]?.owned ?? [] : [];
|
|
4152
|
-
const pruneRoot = usingRealConfig ? HOME :
|
|
4269
|
+
const pruneRoot = usingRealConfig ? HOME : path26.dirname(configDir);
|
|
4153
4270
|
const planTargets = [
|
|
4154
|
-
.../* @__PURE__ */ new Set([...buildPlan(adapter, ctx).map((a) =>
|
|
4155
|
-
].filter((t) => !mergedTargets.has(t) &&
|
|
4271
|
+
.../* @__PURE__ */ new Set([...buildPlan(adapter, ctx).map((a) => path26.resolve(a.target)), ...prevOwned.map((t) => path26.resolve(t))])
|
|
4272
|
+
].filter((t) => !mergedTargets.has(t) && fs19.existsSync(t));
|
|
4156
4273
|
const deleteTargets = planTargets.filter(
|
|
4157
|
-
(t) => !retained.has(t) && !(ctx.preserveEngram &&
|
|
4274
|
+
(t) => !retained.has(t) && !(ctx.preserveEngram && path26.basename(t) === "engram.ts") && isContainedIn(t, pruneRoot)
|
|
4158
4275
|
);
|
|
4159
4276
|
const sharedKept = planTargets.length - deleteTargets.length;
|
|
4160
4277
|
p2.log.step(`${adapter.name} \u2192 ${configDir}`);
|
|
@@ -4164,7 +4281,7 @@ async function runUninstall(opts) {
|
|
|
4164
4281
|
let backup;
|
|
4165
4282
|
try {
|
|
4166
4283
|
backup = createBackup(
|
|
4167
|
-
[...deleteTargets, ...unmerge.map((a) => a.target).filter((t) =>
|
|
4284
|
+
[...deleteTargets, ...unmerge.map((a) => a.target).filter((t) => fs19.existsSync(t))],
|
|
4168
4285
|
`uninstall-${id}`
|
|
4169
4286
|
);
|
|
4170
4287
|
} catch (error) {
|
|
@@ -4174,13 +4291,13 @@ async function runUninstall(opts) {
|
|
|
4174
4291
|
}
|
|
4175
4292
|
if (backup) p2.log.info(`Backup: ${backup.id} (${backup.files.length} archivos)`);
|
|
4176
4293
|
for (const target of deleteTargets) {
|
|
4177
|
-
|
|
4294
|
+
fs19.rmSync(target, { force: true });
|
|
4178
4295
|
pruneEmptyDirs(target, pruneRoot);
|
|
4179
4296
|
}
|
|
4180
4297
|
for (const action of unmerge) {
|
|
4181
4298
|
if (action.kind !== "write") continue;
|
|
4182
4299
|
if (action.content.trim() === "") {
|
|
4183
|
-
|
|
4300
|
+
fs19.rmSync(action.target, { force: true });
|
|
4184
4301
|
} else {
|
|
4185
4302
|
writeText(action.target, action.content);
|
|
4186
4303
|
}
|
|
@@ -4234,12 +4351,12 @@ async function runUninstall(opts) {
|
|
|
4234
4351
|
}
|
|
4235
4352
|
|
|
4236
4353
|
// src/doctor.ts
|
|
4237
|
-
import
|
|
4238
|
-
import
|
|
4354
|
+
import path27 from "path";
|
|
4355
|
+
import fs20 from "fs";
|
|
4239
4356
|
import * as p3 from "@clack/prompts";
|
|
4240
4357
|
function readDoctorTextIfExists(file) {
|
|
4241
4358
|
try {
|
|
4242
|
-
return
|
|
4359
|
+
return fs20.readFileSync(file, "utf8");
|
|
4243
4360
|
} catch (error) {
|
|
4244
4361
|
if (error.code === "ENOENT") return null;
|
|
4245
4362
|
throw error;
|
|
@@ -4263,7 +4380,7 @@ function resolvePlaywrightDoctorState(input) {
|
|
|
4263
4380
|
return { status: "healthy" };
|
|
4264
4381
|
}
|
|
4265
4382
|
function context7KeyConfigured(id, configDir) {
|
|
4266
|
-
const file = id === "codex" ?
|
|
4383
|
+
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
4384
|
const content = readTextIfExists(file);
|
|
4268
4385
|
if (content === null) return null;
|
|
4269
4386
|
const match = /CONTEXT7_API_KEY"?\s*[:=]\s*"([^"]*)"/.exec(content);
|
|
@@ -4283,7 +4400,7 @@ function reportWritingStyle(options, style, mode) {
|
|
|
4283
4400
|
for (const id of runtimes) {
|
|
4284
4401
|
const adapter = id === "pi" ? piAdapter : ADAPTERS[id];
|
|
4285
4402
|
if (adapter === void 0) continue;
|
|
4286
|
-
const configDir = id === "pi" ? options.targetDir === void 0 ? process.env.PI_CODING_AGENT_DIR ??
|
|
4403
|
+
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
4404
|
const file = adapter.paths(configDir).systemPromptFile;
|
|
4288
4405
|
try {
|
|
4289
4406
|
const content = readDoctorTextIfExists(file) ?? "";
|
|
@@ -4298,7 +4415,7 @@ function reportWritingStyle(options, style, mode) {
|
|
|
4298
4415
|
problems++;
|
|
4299
4416
|
}
|
|
4300
4417
|
if (id === "codex") {
|
|
4301
|
-
const override =
|
|
4418
|
+
const override = path27.join(configDir, "AGENTS.override.md");
|
|
4302
4419
|
if ((readDoctorTextIfExists(override) ?? "").trim() !== "") {
|
|
4303
4420
|
p3.log.warn(`Codex: ${override} no vac\xEDo puede ocultar el AGENTS.md gestionado; rev\xEDsalo sin modificarlo autom\xE1ticamente.`);
|
|
4304
4421
|
problems++;
|
|
@@ -4341,13 +4458,13 @@ async function runDoctor(options = {}) {
|
|
|
4341
4458
|
p3.log.success(`Engram: ${version} (${engramBin})`);
|
|
4342
4459
|
}
|
|
4343
4460
|
}
|
|
4344
|
-
const engramDataDir = process.env.ENGRAM_DATA_DIR ??
|
|
4345
|
-
const engramDb =
|
|
4346
|
-
if (
|
|
4347
|
-
const sizeMb = (
|
|
4461
|
+
const engramDataDir = process.env.ENGRAM_DATA_DIR ?? path27.join(HOME, ".engram");
|
|
4462
|
+
const engramDb = path27.join(engramDataDir, "engram.db");
|
|
4463
|
+
if (fs20.existsSync(engramDb)) {
|
|
4464
|
+
const sizeMb = (fs20.statSync(engramDb).size / 1024 / 1024).toFixed(1);
|
|
4348
4465
|
p3.log.info(`Engram DB: ${engramDb} (${sizeMb} MB de memorias \u2014 el stack no la toca JAM\xC1S).`);
|
|
4349
4466
|
}
|
|
4350
|
-
if (!
|
|
4467
|
+
if (!fs20.existsSync(modelMapFile())) p3.log.info("model-map: a\xFAn no creado (se crea en el primer install o con 'models').");
|
|
4351
4468
|
const preferenceErrors = browserPreferenceErrors();
|
|
4352
4469
|
const primaryOwnershipError = primaryModelOwnershipError();
|
|
4353
4470
|
if (primaryOwnershipError !== null) {
|
|
@@ -4429,7 +4546,7 @@ async function runDoctor(options = {}) {
|
|
|
4429
4546
|
p3.log.warn(`${adapter.name}: ${orphans.length} archivos hu\xE9rfanos de versiones previas \u2192 ejecuta 'sync'.`);
|
|
4430
4547
|
problems++;
|
|
4431
4548
|
}
|
|
4432
|
-
if (adapter.id === "codex" &&
|
|
4549
|
+
if (adapter.id === "codex" && fs20.existsSync(path27.join(detection.configDir, "hooks.json"))) {
|
|
4433
4550
|
p3.log.info("Codex: recuerda que los hooks requieren aprobaci\xF3n manual \u2014 verifica con /hooks dentro de codex.");
|
|
4434
4551
|
}
|
|
4435
4552
|
const key = context7KeyConfigured(adapter.id, detection.configDir);
|
|
@@ -4440,15 +4557,15 @@ async function runDoctor(options = {}) {
|
|
|
4440
4557
|
}
|
|
4441
4558
|
|
|
4442
4559
|
// src/update.ts
|
|
4443
|
-
import
|
|
4444
|
-
import
|
|
4560
|
+
import fs23 from "fs";
|
|
4561
|
+
import path30 from "path";
|
|
4445
4562
|
import os3 from "os";
|
|
4446
4563
|
import { execFileSync as execFileSync5 } from "child_process";
|
|
4447
4564
|
import * as p4 from "@clack/prompts";
|
|
4448
4565
|
|
|
4449
4566
|
// src/lib/github.ts
|
|
4450
|
-
import
|
|
4451
|
-
import
|
|
4567
|
+
import fs21 from "fs";
|
|
4568
|
+
import path28 from "path";
|
|
4452
4569
|
import { execFileSync as execFileSync3 } from "child_process";
|
|
4453
4570
|
import os2 from "os";
|
|
4454
4571
|
import { Readable } from "stream";
|
|
@@ -4518,19 +4635,19 @@ async function latestGithubCommit(repo) {
|
|
|
4518
4635
|
}
|
|
4519
4636
|
}
|
|
4520
4637
|
function validateExtractedTree(destDir) {
|
|
4521
|
-
const resolved =
|
|
4638
|
+
const resolved = path28.resolve(destDir);
|
|
4522
4639
|
const walk = (dir) => {
|
|
4523
4640
|
let entries;
|
|
4524
4641
|
try {
|
|
4525
|
-
entries =
|
|
4642
|
+
entries = fs21.readdirSync(dir, { withFileTypes: true });
|
|
4526
4643
|
} catch {
|
|
4527
4644
|
return false;
|
|
4528
4645
|
}
|
|
4529
4646
|
for (const entry of entries) {
|
|
4530
|
-
const full =
|
|
4647
|
+
const full = path28.join(dir, entry.name);
|
|
4531
4648
|
let stat;
|
|
4532
4649
|
try {
|
|
4533
|
-
stat =
|
|
4650
|
+
stat = fs21.lstatSync(full);
|
|
4534
4651
|
} catch {
|
|
4535
4652
|
return false;
|
|
4536
4653
|
}
|
|
@@ -4546,15 +4663,15 @@ function validateExtractedTree(destDir) {
|
|
|
4546
4663
|
}
|
|
4547
4664
|
function resolveTarBin() {
|
|
4548
4665
|
if (process.platform !== "win32") return "tar";
|
|
4549
|
-
const winTar =
|
|
4550
|
-
return
|
|
4666
|
+
const winTar = path28.join(process.env["SystemRoot"] ?? "C:\\Windows", "System32", "tar.exe");
|
|
4667
|
+
return fs21.existsSync(winTar) ? winTar : "tar";
|
|
4551
4668
|
}
|
|
4552
4669
|
async function downloadRepoTarball(repo, sha, destDir, validateSubdir) {
|
|
4553
4670
|
const url = `https://codeload.github.com/${repo}/tar.gz/${sha}`;
|
|
4554
|
-
const tmp =
|
|
4671
|
+
const tmp = path28.join(os2.tmpdir(), `jorgex-tarball-${Date.now()}.tar.gz`);
|
|
4555
4672
|
const fail = (reason) => {
|
|
4556
4673
|
try {
|
|
4557
|
-
|
|
4674
|
+
fs21.rmSync(destDir, { recursive: true, force: true });
|
|
4558
4675
|
} catch {
|
|
4559
4676
|
}
|
|
4560
4677
|
return { ok: false, reason };
|
|
@@ -4573,10 +4690,10 @@ async function downloadRepoTarball(repo, sha, destDir, validateSubdir) {
|
|
|
4573
4690
|
if (!res.body) return fail("respuesta HTTP sin cuerpo");
|
|
4574
4691
|
await pipeline(
|
|
4575
4692
|
Readable.fromWeb(res.body),
|
|
4576
|
-
|
|
4693
|
+
fs21.createWriteStream(tmp)
|
|
4577
4694
|
);
|
|
4578
|
-
|
|
4579
|
-
|
|
4695
|
+
fs21.rmSync(destDir, { recursive: true, force: true });
|
|
4696
|
+
fs21.mkdirSync(destDir, { recursive: true });
|
|
4580
4697
|
try {
|
|
4581
4698
|
execFileSync3(resolveTarBin(), ["-xzf", tmp, "--strip-components=1", "-C", destDir], { stdio: "pipe" });
|
|
4582
4699
|
} catch (err) {
|
|
@@ -4584,30 +4701,30 @@ async function downloadRepoTarball(repo, sha, destDir, validateSubdir) {
|
|
|
4584
4701
|
const detail = (e.stderr?.toString().trim() || e.message || "").split("\n")[0];
|
|
4585
4702
|
return fail(detail ? `tar fall\xF3: ${detail}` : "tar no disponible o fall\xF3 la extracci\xF3n");
|
|
4586
4703
|
}
|
|
4587
|
-
const resolvedDest =
|
|
4588
|
-
const validateRoot = validateSubdir ?
|
|
4704
|
+
const resolvedDest = path28.resolve(destDir);
|
|
4705
|
+
const validateRoot = validateSubdir ? path28.resolve(resolvedDest, validateSubdir) : resolvedDest;
|
|
4589
4706
|
if (validateRoot !== resolvedDest && !isContainedIn(validateRoot, resolvedDest)) {
|
|
4590
4707
|
return fail(`la ruta de validaci\xF3n "${validateSubdir}" escapa del destino`);
|
|
4591
4708
|
}
|
|
4592
|
-
if (
|
|
4709
|
+
if (fs21.existsSync(validateRoot) && !validateExtractedTree(validateRoot)) {
|
|
4593
4710
|
return fail("el \xE1rbol extra\xEDdo contiene symlinks o rutas fuera del destino");
|
|
4594
4711
|
}
|
|
4595
|
-
const validated =
|
|
4712
|
+
const validated = fs21.existsSync(validateRoot);
|
|
4596
4713
|
return { ok: true, validated };
|
|
4597
4714
|
} catch (err) {
|
|
4598
4715
|
const timedOut = err instanceof Error && (err.name === "TimeoutError" || err.name === "AbortError");
|
|
4599
4716
|
return fail(timedOut ? "timeout de descarga (120s)" : err instanceof Error ? `fallo de red: ${err.message}` : "error desconocido");
|
|
4600
4717
|
} finally {
|
|
4601
4718
|
try {
|
|
4602
|
-
|
|
4719
|
+
fs21.rmSync(tmp, { force: true });
|
|
4603
4720
|
} catch {
|
|
4604
4721
|
}
|
|
4605
4722
|
}
|
|
4606
4723
|
}
|
|
4607
4724
|
|
|
4608
4725
|
// src/lib/skill-update.ts
|
|
4609
|
-
import
|
|
4610
|
-
import
|
|
4726
|
+
import fs22 from "fs";
|
|
4727
|
+
import path29 from "path";
|
|
4611
4728
|
import { execFileSync as execFileSync4 } from "child_process";
|
|
4612
4729
|
var PROTECTED_SKILLS = /* @__PURE__ */ new Set([
|
|
4613
4730
|
"agent-delegation",
|
|
@@ -4618,8 +4735,8 @@ var PROTECTED_SKILLS = /* @__PURE__ */ new Set([
|
|
|
4618
4735
|
"xreview"
|
|
4619
4736
|
]);
|
|
4620
4737
|
function sameTextContentNormalized(a, b) {
|
|
4621
|
-
const ba =
|
|
4622
|
-
const bb =
|
|
4738
|
+
const ba = fs22.readFileSync(a);
|
|
4739
|
+
const bb = fs22.readFileSync(b);
|
|
4623
4740
|
if (ba.equals(bb)) return true;
|
|
4624
4741
|
const sa = ba.toString("utf8").replace(/\r\n/g, "\n");
|
|
4625
4742
|
const sb = bb.toString("utf8").replace(/\r\n/g, "\n");
|
|
@@ -4627,10 +4744,10 @@ function sameTextContentNormalized(a, b) {
|
|
|
4627
4744
|
}
|
|
4628
4745
|
function diffSkillDirs(upstreamDir, localDir) {
|
|
4629
4746
|
const upstreamFiles = new Set(
|
|
4630
|
-
listFilesRecursive(upstreamDir).map((f) =>
|
|
4747
|
+
listFilesRecursive(upstreamDir).map((f) => path29.relative(upstreamDir, f))
|
|
4631
4748
|
);
|
|
4632
4749
|
const localFiles = new Set(
|
|
4633
|
-
listFilesRecursive(localDir).map((f) =>
|
|
4750
|
+
listFilesRecursive(localDir).map((f) => path29.relative(localDir, f))
|
|
4634
4751
|
);
|
|
4635
4752
|
const added = [];
|
|
4636
4753
|
const modified = [];
|
|
@@ -4638,7 +4755,7 @@ function diffSkillDirs(upstreamDir, localDir) {
|
|
|
4638
4755
|
for (const rel of upstreamFiles) {
|
|
4639
4756
|
if (!localFiles.has(rel)) {
|
|
4640
4757
|
added.push(rel);
|
|
4641
|
-
} else if (!sameTextContentNormalized(
|
|
4758
|
+
} else if (!sameTextContentNormalized(path29.join(upstreamDir, rel), path29.join(localDir, rel))) {
|
|
4642
4759
|
modified.push(rel);
|
|
4643
4760
|
}
|
|
4644
4761
|
}
|
|
@@ -4700,8 +4817,8 @@ function replaceSkill(name, upstreamSkillDir, newCommit, opts) {
|
|
|
4700
4817
|
if (PROTECTED_SKILLS.has(name)) {
|
|
4701
4818
|
throw new Error(`La skill "${name}" es propia del stack y no se actualiza desde upstream.`);
|
|
4702
4819
|
}
|
|
4703
|
-
const upstreamsFile = upstreamsFilePath ??
|
|
4704
|
-
const raw =
|
|
4820
|
+
const upstreamsFile = upstreamsFilePath ?? path29.join(path29.dirname(stackRoot()), "upstreams.json");
|
|
4821
|
+
const raw = fs22.readFileSync(upstreamsFile, "utf8");
|
|
4705
4822
|
const data = JSON.parse(raw);
|
|
4706
4823
|
const skillEntry = data?.skills?.[name];
|
|
4707
4824
|
if (!skillEntry) {
|
|
@@ -4710,8 +4827,8 @@ function replaceSkill(name, upstreamSkillDir, newCommit, opts) {
|
|
|
4710
4827
|
if (skillEntry.kind === "release") {
|
|
4711
4828
|
throw new Error(`La skill "${name}" es de tipo release y no se actualiza con replaceSkill.`);
|
|
4712
4829
|
}
|
|
4713
|
-
const skillsRoot = localSkillsRoot ??
|
|
4714
|
-
const localSkillDir =
|
|
4830
|
+
const skillsRoot = localSkillsRoot ?? path29.join(stackRoot(), "skills");
|
|
4831
|
+
const localSkillDir = path29.join(skillsRoot, name);
|
|
4715
4832
|
const localFiles = listFilesRecursive(localSkillDir);
|
|
4716
4833
|
if (localFiles.length > 0) {
|
|
4717
4834
|
createBackup(localFiles, `skill-update-${name}`, backupsRoot2);
|
|
@@ -4720,24 +4837,24 @@ function replaceSkill(name, upstreamSkillDir, newCommit, opts) {
|
|
|
4720
4837
|
try {
|
|
4721
4838
|
const upstreamFiles = listFilesRecursive(upstreamSkillDir);
|
|
4722
4839
|
for (const src of upstreamFiles) {
|
|
4723
|
-
const st =
|
|
4840
|
+
const st = fs22.lstatSync(src);
|
|
4724
4841
|
if (st.isSymbolicLink()) {
|
|
4725
4842
|
throw new Error(`Symlink rechazado en upstream de skill "${name}": ${src}`);
|
|
4726
4843
|
}
|
|
4727
|
-
const rel =
|
|
4728
|
-
const dest =
|
|
4729
|
-
ensureDir(
|
|
4844
|
+
const rel = path29.relative(upstreamSkillDir, src);
|
|
4845
|
+
const dest = path29.join(stagingDir, rel);
|
|
4846
|
+
ensureDir(path29.dirname(dest));
|
|
4730
4847
|
copyFile(src, dest);
|
|
4731
4848
|
}
|
|
4732
4849
|
const oldDir = `${localSkillDir}.old-${process.pid}`;
|
|
4733
|
-
if (
|
|
4734
|
-
|
|
4850
|
+
if (fs22.existsSync(localSkillDir)) {
|
|
4851
|
+
fs22.renameSync(localSkillDir, oldDir);
|
|
4735
4852
|
}
|
|
4736
|
-
|
|
4737
|
-
|
|
4853
|
+
fs22.renameSync(stagingDir, localSkillDir);
|
|
4854
|
+
fs22.rmSync(oldDir, { recursive: true, force: true });
|
|
4738
4855
|
} catch (err) {
|
|
4739
4856
|
try {
|
|
4740
|
-
|
|
4857
|
+
fs22.rmSync(stagingDir, { recursive: true, force: true });
|
|
4741
4858
|
} catch {
|
|
4742
4859
|
}
|
|
4743
4860
|
throw err;
|
|
@@ -4751,8 +4868,8 @@ function rateLimitHint(prefix) {
|
|
|
4751
4868
|
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
4869
|
}
|
|
4753
4870
|
function loadUpstreams() {
|
|
4754
|
-
const file =
|
|
4755
|
-
return JSON.parse(
|
|
4871
|
+
const file = path30.join(path30.dirname(stackRoot()), "upstreams.json");
|
|
4872
|
+
return JSON.parse(fs23.readFileSync(file, "utf8"));
|
|
4756
4873
|
}
|
|
4757
4874
|
function skillsToScan(maintainer, upstreams) {
|
|
4758
4875
|
return maintainer ? Object.keys(upstreams.skills) : [];
|
|
@@ -4901,8 +5018,8 @@ function isEngramRunning() {
|
|
|
4901
5018
|
return null;
|
|
4902
5019
|
}
|
|
4903
5020
|
}
|
|
4904
|
-
function isGitClone(projectRoot =
|
|
4905
|
-
return
|
|
5021
|
+
function isGitClone(projectRoot = path30.dirname(stackRoot())) {
|
|
5022
|
+
return fs23.existsSync(path30.join(projectRoot, ".git"));
|
|
4906
5023
|
}
|
|
4907
5024
|
var STACK_METHOD_CLONE = "git pull + pnpm install + pnpm build";
|
|
4908
5025
|
function resolvePnpm() {
|
|
@@ -4912,12 +5029,12 @@ function resolvePnpm() {
|
|
|
4912
5029
|
}
|
|
4913
5030
|
function cleanupTmp(dir) {
|
|
4914
5031
|
try {
|
|
4915
|
-
|
|
5032
|
+
fs23.rmSync(dir, { recursive: true, force: true });
|
|
4916
5033
|
} catch {
|
|
4917
5034
|
}
|
|
4918
5035
|
}
|
|
4919
5036
|
function updateStackGitClone() {
|
|
4920
|
-
const projectRoot =
|
|
5037
|
+
const projectRoot = path30.dirname(stackRoot());
|
|
4921
5038
|
const git = lookPath("git");
|
|
4922
5039
|
if (!git) throw new Error("git no encontrado en PATH.");
|
|
4923
5040
|
const pnpm = resolvePnpm();
|
|
@@ -4934,7 +5051,7 @@ function updateStackGlobal() {
|
|
|
4934
5051
|
execFileSync5(pnpm, ["add", "-g", "jorgex-stack@latest"], { stdio: "inherit" });
|
|
4935
5052
|
}
|
|
4936
5053
|
async function downloadSkillToTemp(repo, sha, skillPath) {
|
|
4937
|
-
const root =
|
|
5054
|
+
const root = fs23.mkdtempSync(path30.join(os3.tmpdir(), "jorgex-skill-"));
|
|
4938
5055
|
try {
|
|
4939
5056
|
const result = await downloadRepoTarball(repo, sha, root, skillPath);
|
|
4940
5057
|
if (!result.ok) {
|
|
@@ -4942,15 +5059,15 @@ async function downloadSkillToTemp(repo, sha, skillPath) {
|
|
|
4942
5059
|
return { error: result.reason };
|
|
4943
5060
|
}
|
|
4944
5061
|
if (skillPath) {
|
|
4945
|
-
const sub =
|
|
5062
|
+
const sub = path30.resolve(path30.join(root, skillPath));
|
|
4946
5063
|
if (!isContainedIn(sub, root)) {
|
|
4947
5064
|
cleanupTmp(root);
|
|
4948
5065
|
return { error: `la ruta "${skillPath}" escapa del directorio temporal` };
|
|
4949
5066
|
}
|
|
4950
|
-
if (
|
|
5067
|
+
if (fs23.existsSync(sub)) return { dir: sub, root };
|
|
4951
5068
|
const lastSeg = skillPath.split("/").pop();
|
|
4952
|
-
const sub2 =
|
|
4953
|
-
if (isContainedIn(sub2, root) &&
|
|
5069
|
+
const sub2 = path30.resolve(path30.join(root, lastSeg));
|
|
5070
|
+
if (isContainedIn(sub2, root) && fs23.existsSync(sub2)) {
|
|
4954
5071
|
if (!validateExtractedTree(sub2)) {
|
|
4955
5072
|
cleanupTmp(root);
|
|
4956
5073
|
return { error: `el sub\xE1rbol "${lastSeg}" contiene symlinks o rutas fuera del destino` };
|
|
@@ -4969,11 +5086,11 @@ async function downloadSkillToTemp(repo, sha, skillPath) {
|
|
|
4969
5086
|
function pruneEngramDbBackups() {
|
|
4970
5087
|
try {
|
|
4971
5088
|
const dir = dataDir();
|
|
4972
|
-
if (!
|
|
4973
|
-
const backups =
|
|
5089
|
+
if (!fs23.existsSync(dir)) return;
|
|
5090
|
+
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
5091
|
for (const old of backups.slice(3)) {
|
|
4975
5092
|
try {
|
|
4976
|
-
|
|
5093
|
+
fs23.rmSync(path30.join(dir, old.name));
|
|
4977
5094
|
} catch {
|
|
4978
5095
|
}
|
|
4979
5096
|
}
|
|
@@ -4981,18 +5098,18 @@ function pruneEngramDbBackups() {
|
|
|
4981
5098
|
}
|
|
4982
5099
|
}
|
|
4983
5100
|
function rotateLockedBinary(binPath, sweepRoot = HOME) {
|
|
4984
|
-
if (!
|
|
4985
|
-
const dir =
|
|
4986
|
-
const base =
|
|
5101
|
+
if (!fs23.existsSync(binPath)) return null;
|
|
5102
|
+
const dir = path30.dirname(binPath);
|
|
5103
|
+
const base = path30.basename(binPath);
|
|
4987
5104
|
const escapedBase = base.replace(/[.*+?^$()|[\]{}\\]/g, "\\$&");
|
|
4988
5105
|
const oldPattern = new RegExp("^" + escapedBase + "\\.old-\\d+$");
|
|
4989
|
-
const resolvedDir =
|
|
4990
|
-
if (resolvedDir ===
|
|
5106
|
+
const resolvedDir = path30.resolve(dir);
|
|
5107
|
+
if (resolvedDir === path30.resolve(sweepRoot) || isContainedIn(resolvedDir, sweepRoot)) {
|
|
4991
5108
|
try {
|
|
4992
|
-
for (const entry of
|
|
5109
|
+
for (const entry of fs23.readdirSync(dir)) {
|
|
4993
5110
|
if (oldPattern.test(entry)) {
|
|
4994
5111
|
try {
|
|
4995
|
-
|
|
5112
|
+
fs23.rmSync(path30.join(dir, entry), { force: true });
|
|
4996
5113
|
} catch {
|
|
4997
5114
|
}
|
|
4998
5115
|
}
|
|
@@ -5000,8 +5117,8 @@ function rotateLockedBinary(binPath, sweepRoot = HOME) {
|
|
|
5000
5117
|
} catch {
|
|
5001
5118
|
}
|
|
5002
5119
|
}
|
|
5003
|
-
const rotated =
|
|
5004
|
-
|
|
5120
|
+
const rotated = path30.join(dir, `${base}.old-${Date.now()}`);
|
|
5121
|
+
fs23.renameSync(binPath, rotated);
|
|
5005
5122
|
return rotated;
|
|
5006
5123
|
}
|
|
5007
5124
|
async function updateEngram(engramRepo, latestVersion) {
|
|
@@ -5010,19 +5127,19 @@ async function updateEngram(engramRepo, latestVersion) {
|
|
|
5010
5127
|
"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
5128
|
);
|
|
5012
5129
|
}
|
|
5013
|
-
const engramDataDir = process.env.ENGRAM_DATA_DIR ??
|
|
5014
|
-
const engramDb =
|
|
5015
|
-
if (
|
|
5130
|
+
const engramDataDir = process.env.ENGRAM_DATA_DIR ?? path30.join(HOME, ".engram");
|
|
5131
|
+
const engramDb = path30.join(engramDataDir, "engram.db");
|
|
5132
|
+
if (fs23.existsSync(engramDb)) {
|
|
5016
5133
|
const doBackup = await p4.confirm({
|
|
5017
5134
|
message: `\xBFHacer backup de la DB de Engram antes de actualizar? (${engramDb})`,
|
|
5018
5135
|
initialValue: true
|
|
5019
5136
|
});
|
|
5020
5137
|
if (!p4.isCancel(doBackup) && doBackup) {
|
|
5021
5138
|
const ts = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
5022
|
-
const dest =
|
|
5139
|
+
const dest = path30.join(dataDir(), `engram-db-backup-${ts}.db`);
|
|
5023
5140
|
try {
|
|
5024
|
-
if (!
|
|
5025
|
-
|
|
5141
|
+
if (!fs23.existsSync(dataDir())) fs23.mkdirSync(dataDir(), { recursive: true });
|
|
5142
|
+
fs23.copyFileSync(engramDb, dest);
|
|
5026
5143
|
p4.log.success(`DB respaldada en ${dest} (la DB original NO se modifica jam\xE1s).`);
|
|
5027
5144
|
pruneEngramDbBackups();
|
|
5028
5145
|
} catch (err) {
|
|
@@ -5072,10 +5189,10 @@ async function updateEngram(engramRepo, latestVersion) {
|
|
|
5072
5189
|
["install", `github.com/Gentleman-Programming/engram/cmd/engram@v${latestVersion}`],
|
|
5073
5190
|
{ stdio: "inherit" }
|
|
5074
5191
|
);
|
|
5075
|
-
const rollbackOk = resolveEngramRollback({ installOk: true, rotated, bin, binExists:
|
|
5192
|
+
const rollbackOk = resolveEngramRollback({ installOk: true, rotated, bin, binExists: fs23.existsSync(bin ?? "") });
|
|
5076
5193
|
if (rollbackOk.action === "restore") {
|
|
5077
5194
|
try {
|
|
5078
|
-
|
|
5195
|
+
fs23.renameSync(rotated, bin);
|
|
5079
5196
|
p4.log.warn(rollbackOk.messages.onRestore);
|
|
5080
5197
|
} catch {
|
|
5081
5198
|
p4.log.warn(rollbackOk.messages.onRenameFail);
|
|
@@ -5083,10 +5200,10 @@ async function updateEngram(engramRepo, latestVersion) {
|
|
|
5083
5200
|
}
|
|
5084
5201
|
return true;
|
|
5085
5202
|
} catch (err) {
|
|
5086
|
-
const rollbackFail = resolveEngramRollback({ installOk: false, rotated, bin, binExists:
|
|
5203
|
+
const rollbackFail = resolveEngramRollback({ installOk: false, rotated, bin, binExists: fs23.existsSync(bin ?? "") });
|
|
5087
5204
|
if (rollbackFail.action === "restore") {
|
|
5088
5205
|
try {
|
|
5089
|
-
|
|
5206
|
+
fs23.renameSync(rotated, bin);
|
|
5090
5207
|
p4.log.info(rollbackFail.messages.onRestore);
|
|
5091
5208
|
} catch {
|
|
5092
5209
|
p4.log.error(rollbackFail.messages.onRenameFail);
|
|
@@ -5356,7 +5473,7 @@ async function runInteractiveUpdate(localVersion, yes, dryRun = false, includeBr
|
|
|
5356
5473
|
continue;
|
|
5357
5474
|
}
|
|
5358
5475
|
const { dir: tmpDir, root: tmpRoot } = tmpResult;
|
|
5359
|
-
const localSkillDir =
|
|
5476
|
+
const localSkillDir = path30.join(stackRoot(), "skills", skillInfo.name);
|
|
5360
5477
|
const diff = renderSkillDiff(tmpDir, localSkillDir);
|
|
5361
5478
|
if (diff) {
|
|
5362
5479
|
p4.log.info(`Diff de ${skillInfo.name}:
|
|
@@ -5442,7 +5559,7 @@ ${diff}`);
|
|
|
5442
5559
|
}
|
|
5443
5560
|
|
|
5444
5561
|
// src/models-picker.ts
|
|
5445
|
-
import
|
|
5562
|
+
import path31 from "path";
|
|
5446
5563
|
import * as p5 from "@clack/prompts";
|
|
5447
5564
|
var TIERS = ["strong", "standard", "cheap"];
|
|
5448
5565
|
var EFFORTS = ["low", "medium", "high", "xhigh"];
|
|
@@ -5468,7 +5585,7 @@ function opencodeLiveModels(binPath) {
|
|
|
5468
5585
|
}
|
|
5469
5586
|
function agentsByTier() {
|
|
5470
5587
|
const grouped = { strong: [], standard: [], cheap: [] };
|
|
5471
|
-
for (const agent of loadCanonicalAgents(
|
|
5588
|
+
for (const agent of loadCanonicalAgents(path31.join(stackRoot(), "agents"))) {
|
|
5472
5589
|
if (agent.mode === "subagent") grouped[agent.tier].push(agent.name);
|
|
5473
5590
|
}
|
|
5474
5591
|
return grouped;
|
|
@@ -5653,16 +5770,16 @@ function cancelled() {
|
|
|
5653
5770
|
}
|
|
5654
5771
|
|
|
5655
5772
|
// src/lib/release.ts
|
|
5656
|
-
import
|
|
5657
|
-
import
|
|
5773
|
+
import fs24 from "fs";
|
|
5774
|
+
import path32 from "path";
|
|
5658
5775
|
import { execFileSync as execFileSync6 } from "child_process";
|
|
5659
5776
|
import { fileURLToPath } from "url";
|
|
5660
5777
|
function findPackageJson() {
|
|
5661
|
-
let dir =
|
|
5778
|
+
let dir = path32.dirname(fileURLToPath(import.meta.url));
|
|
5662
5779
|
for (let i = 0; i < 6; i++) {
|
|
5663
|
-
const candidate =
|
|
5664
|
-
if (
|
|
5665
|
-
dir =
|
|
5780
|
+
const candidate = path32.join(dir, "package.json");
|
|
5781
|
+
if (fs24.existsSync(candidate)) return candidate;
|
|
5782
|
+
dir = path32.dirname(dir);
|
|
5666
5783
|
}
|
|
5667
5784
|
throw new Error("No se encontr\xF3 package.json cerca del CLI.");
|
|
5668
5785
|
}
|
|
@@ -5671,7 +5788,7 @@ function readPackageVersion() {
|
|
|
5671
5788
|
}
|
|
5672
5789
|
function readPackageMetadata() {
|
|
5673
5790
|
const packageJson = findPackageJson();
|
|
5674
|
-
const raw =
|
|
5791
|
+
const raw = fs24.readFileSync(packageJson, "utf8");
|
|
5675
5792
|
const parsed = JSON.parse(raw);
|
|
5676
5793
|
const name = typeof parsed.name === "string" ? parsed.name.trim() : "";
|
|
5677
5794
|
const version = typeof parsed.version === "string" ? parsed.version.trim() : "";
|
|
@@ -5683,8 +5800,8 @@ function readPackageMetadata() {
|
|
|
5683
5800
|
|
|
5684
5801
|
// src/lib/pi-runtime.ts
|
|
5685
5802
|
import os4 from "os";
|
|
5686
|
-
import
|
|
5687
|
-
import
|
|
5803
|
+
import path33 from "path";
|
|
5804
|
+
import fs25 from "fs";
|
|
5688
5805
|
import { spawnSync } from "child_process";
|
|
5689
5806
|
import { createHash as createHash2 } from "crypto";
|
|
5690
5807
|
|
|
@@ -5692,16 +5809,16 @@ import { createHash as createHash2 } from "crypto";
|
|
|
5692
5809
|
var pi_runtime_pin_default = {
|
|
5693
5810
|
package: {
|
|
5694
5811
|
name: "jorgex-pi",
|
|
5695
|
-
version: "0.8.
|
|
5696
|
-
source: "npm:jorgex-pi@0.8.
|
|
5812
|
+
version: "0.8.18",
|
|
5813
|
+
source: "npm:jorgex-pi@0.8.18"
|
|
5697
5814
|
},
|
|
5698
5815
|
provenance: {
|
|
5699
|
-
commit: "
|
|
5816
|
+
commit: "61d676b300707d61840165616f0880d8c6588ce7"
|
|
5700
5817
|
},
|
|
5701
5818
|
tarball: {
|
|
5702
|
-
bytes:
|
|
5703
|
-
sha256: "
|
|
5704
|
-
sha512: "
|
|
5819
|
+
bytes: 89122709,
|
|
5820
|
+
sha256: "02e5577d95d19ae28de55c170d4b6b48e682fbe741c8eec26cd79587da2f829e",
|
|
5821
|
+
sha512: "a9fa60be18b6a84dca6703bf38a1462482dd3ea00d9eb82aead06e9a86b3d92ce09b68da2968cb760d6b3ad8d5b2b36d53889bed227e7ea6791932ce25153220"
|
|
5705
5822
|
}
|
|
5706
5823
|
};
|
|
5707
5824
|
|
|
@@ -5851,14 +5968,14 @@ function healthyDoctor(stdout, stderr, packageRunner, candidate) {
|
|
|
5851
5968
|
if (record === null || typeof record !== "object" || Array.isArray(record)) return false;
|
|
5852
5969
|
const packageValue = Reflect.get(record, "package");
|
|
5853
5970
|
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]) &&
|
|
5971
|
+
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
5972
|
} catch {
|
|
5856
5973
|
return false;
|
|
5857
5974
|
}
|
|
5858
5975
|
}
|
|
5859
5976
|
function installPiFromVerifiedTarball(input, deps) {
|
|
5860
5977
|
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 ?
|
|
5978
|
+
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
5979
|
const artifact = deps.download(destination);
|
|
5863
5980
|
if (artifact.bytes !== input.candidate.bytes || artifact.sha256 !== input.candidate.sha256 || artifact.sha512 !== input.candidate.sha512) {
|
|
5864
5981
|
return { kind: "blocked", reason: "tarball-integrity" };
|
|
@@ -5866,7 +5983,7 @@ function installPiFromVerifiedTarball(input, deps) {
|
|
|
5866
5983
|
deps.backupSettings();
|
|
5867
5984
|
const scope = {
|
|
5868
5985
|
kind: input.targetDir === void 0 ? "real" : "target-dir",
|
|
5869
|
-
codingAgentDir:
|
|
5986
|
+
codingAgentDir: path33.resolve(paths.codingAgentDir)
|
|
5870
5987
|
};
|
|
5871
5988
|
const installing = flatCandidateReceipt(input.candidate, scope, "installing", input.engramBin);
|
|
5872
5989
|
deps.writeReceiptAtomic(`${JSON.stringify(installing)}
|
|
@@ -5895,30 +6012,30 @@ function installPiFromVerifiedTarball(input, deps) {
|
|
|
5895
6012
|
return { kind: "installed", receipt };
|
|
5896
6013
|
}
|
|
5897
6014
|
function runtimePath(piExecutable) {
|
|
5898
|
-
const entries = process.platform === "win32" ? [piExecutable === void 0 ? null :
|
|
5899
|
-
return [...new Set(entries.filter((entry) => entry !== null))].join(
|
|
6015
|
+
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"];
|
|
6016
|
+
return [...new Set(entries.filter((entry) => entry !== null))].join(path33.delimiter);
|
|
5900
6017
|
}
|
|
5901
6018
|
function targetPaths(targetDir, engramBin, piExecutable) {
|
|
5902
|
-
const root =
|
|
5903
|
-
const codingAgentDir =
|
|
5904
|
-
const home =
|
|
5905
|
-
const temporary =
|
|
6019
|
+
const root = path33.resolve(targetDir);
|
|
6020
|
+
const codingAgentDir = path33.join(root, "pi-agent");
|
|
6021
|
+
const home = path33.join(root, "home");
|
|
6022
|
+
const temporary = path33.join(root, "tmp");
|
|
5906
6023
|
return {
|
|
5907
6024
|
codingAgentDir,
|
|
5908
|
-
receiptPath:
|
|
5909
|
-
packageRunner:
|
|
6025
|
+
receiptPath: path33.join(root, "state", "pi-receipt.json"),
|
|
6026
|
+
packageRunner: path33.join(codingAgentDir, "npm", "node_modules", "jorgex-pi", "bin", "jorgex-pi.mjs"),
|
|
5910
6027
|
environment: {
|
|
5911
6028
|
HOME: home,
|
|
5912
6029
|
USERPROFILE: home,
|
|
5913
|
-
APPDATA:
|
|
5914
|
-
LOCALAPPDATA:
|
|
5915
|
-
XDG_CONFIG_HOME:
|
|
5916
|
-
XDG_DATA_HOME:
|
|
5917
|
-
XDG_CACHE_HOME:
|
|
6030
|
+
APPDATA: path33.join(root, "appdata"),
|
|
6031
|
+
LOCALAPPDATA: path33.join(root, "localappdata"),
|
|
6032
|
+
XDG_CONFIG_HOME: path33.join(root, "xdg-config"),
|
|
6033
|
+
XDG_DATA_HOME: path33.join(root, "xdg-data"),
|
|
6034
|
+
XDG_CACHE_HOME: path33.join(root, "xdg-cache"),
|
|
5918
6035
|
TEMP: temporary,
|
|
5919
6036
|
TMP: temporary,
|
|
5920
6037
|
TMPDIR: temporary,
|
|
5921
|
-
npm_config_cache:
|
|
6038
|
+
npm_config_cache: path33.join(root, "npm-cache"),
|
|
5922
6039
|
NPM_CONFIG_IGNORE_SCRIPTS: "true",
|
|
5923
6040
|
NPM_CONFIG_UPDATE_NOTIFIER: "false",
|
|
5924
6041
|
PI_CODING_AGENT_DIR: codingAgentDir,
|
|
@@ -5929,15 +6046,15 @@ function targetPaths(targetDir, engramBin, piExecutable) {
|
|
|
5929
6046
|
}
|
|
5930
6047
|
function userPaths(engramBin, piExecutable) {
|
|
5931
6048
|
const home = os4.homedir();
|
|
5932
|
-
const codingAgentDir = process.env.PI_CODING_AGENT_DIR ??
|
|
6049
|
+
const codingAgentDir = process.env.PI_CODING_AGENT_DIR ?? path33.join(home, ".pi", "agent");
|
|
5933
6050
|
return {
|
|
5934
6051
|
codingAgentDir,
|
|
5935
|
-
receiptPath:
|
|
5936
|
-
packageRunner:
|
|
6052
|
+
receiptPath: path33.join(dataDir(), "pi-receipt.json"),
|
|
6053
|
+
packageRunner: path33.join(codingAgentDir, "npm", "node_modules", "jorgex-pi", "bin", "jorgex-pi.mjs"),
|
|
5937
6054
|
environment: {
|
|
5938
6055
|
HOME: home,
|
|
5939
|
-
XDG_CONFIG_HOME: process.env.XDG_CONFIG_HOME ??
|
|
5940
|
-
XDG_CACHE_HOME: process.env.XDG_CACHE_HOME ??
|
|
6056
|
+
XDG_CONFIG_HOME: process.env.XDG_CONFIG_HOME ?? path33.join(home, ".config"),
|
|
6057
|
+
XDG_CACHE_HOME: process.env.XDG_CACHE_HOME ?? path33.join(home, ".cache"),
|
|
5941
6058
|
TMPDIR: os4.tmpdir(),
|
|
5942
6059
|
NPM_CONFIG_IGNORE_SCRIPTS: "true",
|
|
5943
6060
|
NPM_CONFIG_UPDATE_NOTIFIER: "false",
|
|
@@ -5965,7 +6082,7 @@ function runPiRuntime(input, deps) {
|
|
|
5965
6082
|
return { kind: "blocked", reason: "tarball-integrity" };
|
|
5966
6083
|
}
|
|
5967
6084
|
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(
|
|
6085
|
+
const settingsJson = deps.readSettings(path33.join(paths.codingAgentDir, "settings.json"));
|
|
5969
6086
|
const receiptJson = deps.readReceipt(paths.receiptPath);
|
|
5970
6087
|
const lifecycleInput = {
|
|
5971
6088
|
candidate: PI_RUNTIME_CANDIDATE,
|
|
@@ -6023,19 +6140,19 @@ function runPiRuntime(input, deps) {
|
|
|
6023
6140
|
return result;
|
|
6024
6141
|
}
|
|
6025
6142
|
function readJsonFile(file) {
|
|
6026
|
-
return JSON.parse(
|
|
6143
|
+
return JSON.parse(fs25.readFileSync(file, "utf8"));
|
|
6027
6144
|
}
|
|
6028
6145
|
function packageVersionFromExecutable(executable) {
|
|
6029
6146
|
let current;
|
|
6030
6147
|
try {
|
|
6031
|
-
current =
|
|
6148
|
+
current = path33.dirname(fs25.realpathSync(executable));
|
|
6032
6149
|
} catch {
|
|
6033
6150
|
return null;
|
|
6034
6151
|
}
|
|
6035
6152
|
for (let depth = 0; depth < 8; depth++) {
|
|
6036
6153
|
const manifests = [
|
|
6037
|
-
|
|
6038
|
-
|
|
6154
|
+
path33.join(current, "package.json"),
|
|
6155
|
+
path33.join(current, "node_modules", "@earendil-works", "pi-coding-agent", "package.json")
|
|
6039
6156
|
];
|
|
6040
6157
|
for (const manifest of manifests) {
|
|
6041
6158
|
try {
|
|
@@ -6046,7 +6163,7 @@ function packageVersionFromExecutable(executable) {
|
|
|
6046
6163
|
} catch {
|
|
6047
6164
|
}
|
|
6048
6165
|
}
|
|
6049
|
-
const parent =
|
|
6166
|
+
const parent = path33.dirname(current);
|
|
6050
6167
|
if (parent === current) break;
|
|
6051
6168
|
current = parent;
|
|
6052
6169
|
}
|
|
@@ -6061,35 +6178,35 @@ function detectPiRuntime() {
|
|
|
6061
6178
|
installed: executable !== null,
|
|
6062
6179
|
executable,
|
|
6063
6180
|
version: executable === null ? null : packageVersionFromExecutable(executable),
|
|
6064
|
-
codingAgentDir: process.env.PI_CODING_AGENT_DIR ??
|
|
6181
|
+
codingAgentDir: process.env.PI_CODING_AGENT_DIR ?? path33.join(home, ".pi", "agent")
|
|
6065
6182
|
};
|
|
6066
6183
|
}
|
|
6067
6184
|
function hasManagedPiRuntime(targetDir) {
|
|
6068
|
-
const receipt = targetDir === void 0 ?
|
|
6069
|
-
return
|
|
6185
|
+
const receipt = targetDir === void 0 ? path33.join(dataDir(), "pi-receipt.json") : path33.join(path33.resolve(targetDir), "state", "pi-receipt.json");
|
|
6186
|
+
return fs25.statSync(receipt, { throwIfNoEntry: false })?.isFile() === true;
|
|
6070
6187
|
}
|
|
6071
6188
|
function resolvePiEngramBin(targetDir) {
|
|
6072
6189
|
if (targetDir === void 0) return detectEngram();
|
|
6073
|
-
const candidate =
|
|
6074
|
-
return
|
|
6190
|
+
const candidate = path33.join(path33.resolve(targetDir), "bin", process.platform === "win32" ? "engram.exe" : "engram");
|
|
6191
|
+
return fs25.statSync(candidate, { throwIfNoEntry: false })?.isFile() ? candidate : null;
|
|
6075
6192
|
}
|
|
6076
6193
|
function readOptional(file, fallback) {
|
|
6077
6194
|
try {
|
|
6078
|
-
return
|
|
6195
|
+
return fs25.readFileSync(file, "utf8");
|
|
6079
6196
|
} catch (error) {
|
|
6080
6197
|
if (error.code === "ENOENT") return fallback;
|
|
6081
6198
|
throw error;
|
|
6082
6199
|
}
|
|
6083
6200
|
}
|
|
6084
6201
|
function hashPiTarball(file) {
|
|
6085
|
-
const descriptor =
|
|
6202
|
+
const descriptor = fs25.openSync(file, "r");
|
|
6086
6203
|
const sha2562 = createHash2("sha256");
|
|
6087
6204
|
const sha512 = createHash2("sha512");
|
|
6088
6205
|
const buffer = Buffer.allocUnsafe(1024 * 1024);
|
|
6089
6206
|
let bytes = 0;
|
|
6090
6207
|
try {
|
|
6091
6208
|
while (true) {
|
|
6092
|
-
const read =
|
|
6209
|
+
const read = fs25.readSync(descriptor, buffer, 0, buffer.length, null);
|
|
6093
6210
|
if (read === 0) break;
|
|
6094
6211
|
bytes += read;
|
|
6095
6212
|
const chunk = buffer.subarray(0, read);
|
|
@@ -6097,27 +6214,27 @@ function hashPiTarball(file) {
|
|
|
6097
6214
|
sha512.update(chunk);
|
|
6098
6215
|
}
|
|
6099
6216
|
} finally {
|
|
6100
|
-
|
|
6217
|
+
fs25.closeSync(descriptor);
|
|
6101
6218
|
}
|
|
6102
6219
|
return { path: file, bytes, sha256: sha2562.digest("hex"), sha512: sha512.digest("hex") };
|
|
6103
6220
|
}
|
|
6104
6221
|
async function acquirePiTarball(destination) {
|
|
6105
|
-
const existing =
|
|
6222
|
+
const existing = fs25.statSync(destination, { throwIfNoEntry: false });
|
|
6106
6223
|
if (existing?.isFile()) {
|
|
6107
6224
|
const observed = hashPiTarball(destination);
|
|
6108
6225
|
if (observed.bytes === PI_RUNTIME_CANDIDATE.tarball.bytes && observed.sha256 === PI_RUNTIME_CANDIDATE.tarball.sha256 && observed.sha512 === PI_RUNTIME_CANDIDATE.tarball.sha512) {
|
|
6109
6226
|
return observed;
|
|
6110
6227
|
}
|
|
6111
|
-
|
|
6228
|
+
fs25.rmSync(destination, { force: true });
|
|
6112
6229
|
}
|
|
6113
|
-
|
|
6230
|
+
fs25.mkdirSync(path33.dirname(destination), { recursive: true });
|
|
6114
6231
|
const partial = `${destination}.partial-${process.pid}`;
|
|
6115
6232
|
const response = await fetch(`https://registry.npmjs.org/jorgex-pi/-/jorgex-pi-${PI_RUNTIME_CANDIDATE.package.version}.tgz`, {
|
|
6116
6233
|
redirect: "error",
|
|
6117
6234
|
headers: { accept: "application/octet-stream" }
|
|
6118
6235
|
});
|
|
6119
6236
|
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 =
|
|
6237
|
+
const descriptor = fs25.openSync(partial, "wx", 384);
|
|
6121
6238
|
let bytes = 0;
|
|
6122
6239
|
try {
|
|
6123
6240
|
for await (const chunk of response.body) {
|
|
@@ -6125,15 +6242,15 @@ async function acquirePiTarball(destination) {
|
|
|
6125
6242
|
bytes += buffer.length;
|
|
6126
6243
|
if (bytes > PI_RUNTIME_CANDIDATE.tarball.bytes) throw new Error("El tarball de jorgex-pi excede el tama\xF1o fijado.");
|
|
6127
6244
|
let offset = 0;
|
|
6128
|
-
while (offset < buffer.length) offset +=
|
|
6245
|
+
while (offset < buffer.length) offset += fs25.writeSync(descriptor, buffer, offset);
|
|
6129
6246
|
}
|
|
6130
6247
|
} catch (error) {
|
|
6131
|
-
|
|
6132
|
-
|
|
6248
|
+
fs25.closeSync(descriptor);
|
|
6249
|
+
fs25.rmSync(partial, { force: true });
|
|
6133
6250
|
throw error;
|
|
6134
6251
|
}
|
|
6135
|
-
|
|
6136
|
-
|
|
6252
|
+
fs25.closeSync(descriptor);
|
|
6253
|
+
fs25.renameSync(partial, destination);
|
|
6137
6254
|
return hashPiTarball(destination);
|
|
6138
6255
|
}
|
|
6139
6256
|
function runProcess(invocation) {
|
|
@@ -6183,7 +6300,7 @@ async function runPiRuntimeSystem(input) {
|
|
|
6183
6300
|
remedy: "Instala Engram o configura un ENGRAM_BIN absoluto antes de reintentar."
|
|
6184
6301
|
};
|
|
6185
6302
|
}
|
|
6186
|
-
const destination = input.targetDir === void 0 ?
|
|
6303
|
+
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
6304
|
let artifact;
|
|
6188
6305
|
try {
|
|
6189
6306
|
artifact = await acquirePiTarball(destination);
|
|
@@ -6203,18 +6320,18 @@ async function runPiRuntimeSystem(input) {
|
|
|
6203
6320
|
}, {
|
|
6204
6321
|
download: () => artifact,
|
|
6205
6322
|
backupSettings: () => createBackup(
|
|
6206
|
-
[
|
|
6323
|
+
[path33.join(paths.codingAgentDir, "settings.json")],
|
|
6207
6324
|
"pi-package-install",
|
|
6208
|
-
input.targetDir === void 0 ? void 0 :
|
|
6325
|
+
input.targetDir === void 0 ? void 0 : path33.join(path33.resolve(input.targetDir), "backups")
|
|
6209
6326
|
),
|
|
6210
6327
|
run: runProcess,
|
|
6211
|
-
readSettings: () => readOptional(
|
|
6212
|
-
rewriteSettings: (content) => writeText(
|
|
6328
|
+
readSettings: () => readOptional(path33.join(paths.codingAgentDir, "settings.json"), '{"packages":[]}'),
|
|
6329
|
+
rewriteSettings: (content) => writeText(path33.join(paths.codingAgentDir, "settings.json"), `${content}
|
|
6213
6330
|
`),
|
|
6214
6331
|
writeReceiptAtomic: (content) => writeText(paths.receiptPath, content)
|
|
6215
6332
|
});
|
|
6216
6333
|
}
|
|
6217
|
-
const packageRoot =
|
|
6334
|
+
const packageRoot = path33.dirname(path33.dirname(paths.packageRunner));
|
|
6218
6335
|
const writeReceipt = (receipt) => {
|
|
6219
6336
|
writeText(paths.receiptPath, `${JSON.stringify(receipt, null, 2)}
|
|
6220
6337
|
`);
|
|
@@ -6232,13 +6349,13 @@ async function runPiRuntimeSystem(input) {
|
|
|
6232
6349
|
value,
|
|
6233
6350
|
{
|
|
6234
6351
|
backupSettings: () => createBackup(
|
|
6235
|
-
[
|
|
6352
|
+
[path33.join(paths.codingAgentDir, "settings.json")],
|
|
6236
6353
|
"pi-package-uninstall",
|
|
6237
|
-
input.targetDir === void 0 ? void 0 :
|
|
6354
|
+
input.targetDir === void 0 ? void 0 : path33.join(path33.resolve(input.targetDir), "backups")
|
|
6238
6355
|
),
|
|
6239
6356
|
run: runProcess,
|
|
6240
|
-
isPackageAbsent: () => !
|
|
6241
|
-
deleteReceipt: () =>
|
|
6357
|
+
isPackageAbsent: () => !fs25.existsSync(packageRoot),
|
|
6358
|
+
deleteReceipt: () => fs25.rmSync(paths.receiptPath, { force: true })
|
|
6242
6359
|
}
|
|
6243
6360
|
)
|
|
6244
6361
|
};
|
|
@@ -6318,6 +6435,13 @@ async function runManagedPiSystem(input) {
|
|
|
6318
6435
|
...runtimeInput
|
|
6319
6436
|
} = input;
|
|
6320
6437
|
const readsStyle = input.operation !== "uninstall" && input.operation !== "models";
|
|
6438
|
+
if (input.operation !== "models") {
|
|
6439
|
+
try {
|
|
6440
|
+
assertSystemPromptFile(piSystemPromptFile(input.targetDir), input.targetDir);
|
|
6441
|
+
} catch (error) {
|
|
6442
|
+
return { kind: "blocked", reason: "projection-prompt-markers", remedy: error instanceof Error ? error.message : String(error) };
|
|
6443
|
+
}
|
|
6444
|
+
}
|
|
6321
6445
|
const preparedStyle = readsStyle && suppliedStyle === void 0 ? prepareWritingStyle(resolveWritingStyleFile({ targetDir: input.targetDir }), { rootDir: input.targetDir }) : void 0;
|
|
6322
6446
|
const style = readsStyle ? suppliedStyle ?? preparedStyle : void 0;
|
|
6323
6447
|
const mode = readsStyle ? writingStyleMode ?? (input.targetDir === void 0 ? loadInstallModePreference().mode : "human") : "human";
|
|
@@ -6371,7 +6495,7 @@ async function runManagedPiSystem(input) {
|
|
|
6371
6495
|
}
|
|
6372
6496
|
|
|
6373
6497
|
// src/lib/quality-runner.ts
|
|
6374
|
-
import
|
|
6498
|
+
import path34 from "path";
|
|
6375
6499
|
import { spawn, spawnSync as spawnSync2 } from "child_process";
|
|
6376
6500
|
function normalizeLimit(value, name) {
|
|
6377
6501
|
if (value === void 0) return void 0;
|
|
@@ -6503,7 +6627,7 @@ function killProcessTree(child) {
|
|
|
6503
6627
|
if (pid === void 0) return;
|
|
6504
6628
|
if (process.platform === "win32") {
|
|
6505
6629
|
const systemRoot = process.env.SystemRoot ?? process.env.WINDIR ?? "C:\\Windows";
|
|
6506
|
-
const taskkill =
|
|
6630
|
+
const taskkill = path34.join(systemRoot, "System32", "taskkill.exe");
|
|
6507
6631
|
const result = spawnSync2(taskkill, ["/pid", String(pid), "/t", "/f"], {
|
|
6508
6632
|
shell: false,
|
|
6509
6633
|
stdio: "ignore",
|
|
@@ -6749,9 +6873,9 @@ async function runQualityPlan(input) {
|
|
|
6749
6873
|
}
|
|
6750
6874
|
|
|
6751
6875
|
// src/lib/engram-install.ts
|
|
6752
|
-
import
|
|
6876
|
+
import fs26 from "fs";
|
|
6753
6877
|
import os5 from "os";
|
|
6754
|
-
import
|
|
6878
|
+
import path35 from "path";
|
|
6755
6879
|
import { execFileSync as execFileSync7 } from "child_process";
|
|
6756
6880
|
import { createHash as createHash3 } from "crypto";
|
|
6757
6881
|
|
|
@@ -6796,15 +6920,15 @@ var engram_release_default = {
|
|
|
6796
6920
|
async function installMissingEngram(options = {}) {
|
|
6797
6921
|
const platform = options.platform ?? process.platform;
|
|
6798
6922
|
const homeDir = options.homeDir ?? os5.homedir();
|
|
6799
|
-
if (!
|
|
6923
|
+
if (!path35.isAbsolute(homeDir)) return { ok: false, reason: "El directorio personal debe ser absoluto." };
|
|
6800
6924
|
const binaryName = platform === "win32" ? "engram.exe" : "engram";
|
|
6801
|
-
const bin =
|
|
6925
|
+
const bin = path35.join(homeDir, ".local", "bin", binaryName);
|
|
6802
6926
|
let staging;
|
|
6803
6927
|
try {
|
|
6804
|
-
const existing =
|
|
6928
|
+
const existing = fs26.lstatSync(bin, { throwIfNoEntry: false });
|
|
6805
6929
|
if (existing) {
|
|
6806
|
-
if (!
|
|
6807
|
-
|
|
6930
|
+
if (!fs26.statSync(bin).isFile()) throw new Error(`El destino Engram no es un archivo: ${bin}`);
|
|
6931
|
+
fs26.accessSync(bin, platform === "win32" ? fs26.constants.F_OK : fs26.constants.X_OK);
|
|
6808
6932
|
return { ok: true, bin };
|
|
6809
6933
|
}
|
|
6810
6934
|
const key = `${platform === "win32" ? "windows" : platform}_${options.arch ?? process.arch}`;
|
|
@@ -6833,24 +6957,24 @@ async function installMissingEngram(options = {}) {
|
|
|
6833
6957
|
if (size !== asset.size || createHash3("sha256").update(archive).digest("hex") !== asset.sha256) {
|
|
6834
6958
|
throw new Error("Engram: tama\xF1o o hash SHA-256 no coincide con el artefacto aprobado.");
|
|
6835
6959
|
}
|
|
6836
|
-
|
|
6837
|
-
staging =
|
|
6838
|
-
const archivePath =
|
|
6839
|
-
|
|
6960
|
+
fs26.mkdirSync(path35.dirname(bin), { recursive: true });
|
|
6961
|
+
staging = fs26.mkdtempSync(path35.join(path35.dirname(bin), ".engram-install-"));
|
|
6962
|
+
const archivePath = path35.join(staging, asset.name);
|
|
6963
|
+
fs26.writeFileSync(archivePath, archive, { flag: "wx", mode: 384 });
|
|
6840
6964
|
const binary = execFileSync7(resolveTarBin(), ["-xOf", archivePath, binaryName], {
|
|
6841
6965
|
maxBuffer: 64 * 1024 * 1024,
|
|
6842
6966
|
timeout: 3e4,
|
|
6843
6967
|
stdio: ["ignore", "pipe", "pipe"]
|
|
6844
6968
|
});
|
|
6845
6969
|
if (binary.length === 0) throw new Error("El artefacto Engram no contiene un binario v\xE1lido.");
|
|
6846
|
-
const stagedBin =
|
|
6847
|
-
|
|
6848
|
-
|
|
6970
|
+
const stagedBin = path35.join(staging, binaryName);
|
|
6971
|
+
fs26.writeFileSync(stagedBin, binary, { flag: "wx", mode: 493 });
|
|
6972
|
+
fs26.linkSync(stagedBin, bin);
|
|
6849
6973
|
return { ok: true, bin };
|
|
6850
6974
|
} catch (error) {
|
|
6851
6975
|
return { ok: false, reason: error instanceof Error ? error.message : String(error) };
|
|
6852
6976
|
} finally {
|
|
6853
|
-
if (staging)
|
|
6977
|
+
if (staging) fs26.rmSync(staging, { recursive: true, force: true });
|
|
6854
6978
|
}
|
|
6855
6979
|
}
|
|
6856
6980
|
|
|
@@ -7109,6 +7233,18 @@ async function resolveDevtoolsMcpSelection(command, flags, runtimes) {
|
|
|
7109
7233
|
const enabled = new Set(selected);
|
|
7110
7234
|
return Object.fromEntries(runtimes.map((runtime) => [runtime, enabled.has(runtime)]));
|
|
7111
7235
|
}
|
|
7236
|
+
function assertSelectedPromptFiles(runtimes, targetDir) {
|
|
7237
|
+
for (const runtime of runtimes) {
|
|
7238
|
+
if (runtime === "pi") assertSystemPromptFile(piSystemPromptFile(targetDir), targetDir);
|
|
7239
|
+
else {
|
|
7240
|
+
const adapter = ADAPTERS[runtime];
|
|
7241
|
+
if (!adapter) continue;
|
|
7242
|
+
const detection = adapter.detect();
|
|
7243
|
+
if (targetDir === void 0 && !detection.installed) continue;
|
|
7244
|
+
assertSystemPromptFile(adapter.paths(targetDir ?? detection.configDir).systemPromptFile, targetDir);
|
|
7245
|
+
}
|
|
7246
|
+
}
|
|
7247
|
+
}
|
|
7112
7248
|
function parseCliArgs(argv) {
|
|
7113
7249
|
const [first, ...rest] = argv;
|
|
7114
7250
|
const isCommand = COMMANDS.includes(first ?? "install");
|
|
@@ -7335,7 +7471,7 @@ Flags disponibles: jorgex-stack --help`
|
|
|
7335
7471
|
return;
|
|
7336
7472
|
}
|
|
7337
7473
|
try {
|
|
7338
|
-
const plan = JSON.parse(
|
|
7474
|
+
const plan = JSON.parse(fs27.readFileSync(flags.positional[0], "utf8"));
|
|
7339
7475
|
const result = await runQualityPlan(plan);
|
|
7340
7476
|
const serialized = serializeQualityReceipt(result.receipt);
|
|
7341
7477
|
if (flags.receipt === void 0) process.stdout.write(`${serialized}
|
|
@@ -7363,6 +7499,7 @@ Flags disponibles: jorgex-stack --help`
|
|
|
7363
7499
|
let completed = false;
|
|
7364
7500
|
p6.intro(`jorgex-stack ${command}${flags.dryRun ? " (dry-run)" : ""}`);
|
|
7365
7501
|
try {
|
|
7502
|
+
assertSelectedPromptFiles(runtimes, flags.targetDir);
|
|
7366
7503
|
const writingStyle = prepareWritingStyle(resolveWritingStyleFile({ targetDir: flags.targetDir }), { rootDir: flags.targetDir });
|
|
7367
7504
|
if (flags.targetDir === void 0) {
|
|
7368
7505
|
const errors = browserPreferenceErrors();
|
|
@@ -7468,6 +7605,13 @@ Flags disponibles: jorgex-stack --help`
|
|
|
7468
7605
|
process.exitCode = 1;
|
|
7469
7606
|
return;
|
|
7470
7607
|
}
|
|
7608
|
+
try {
|
|
7609
|
+
assertSelectedPromptFiles(runtimes, flags.targetDir);
|
|
7610
|
+
} catch (error) {
|
|
7611
|
+
p6.log.error(error instanceof Error ? error.message : String(error));
|
|
7612
|
+
process.exitCode = 1;
|
|
7613
|
+
return;
|
|
7614
|
+
}
|
|
7471
7615
|
const fileRuntimes = runtimes.filter(isFileManagedRuntime);
|
|
7472
7616
|
let exitCode = fileRuntimes.length > 0 || flags.removePlaywright ? await runUninstall({
|
|
7473
7617
|
runtimes: fileRuntimes,
|
|
@@ -7508,6 +7652,13 @@ Flags disponibles: jorgex-stack --help`
|
|
|
7508
7652
|
}
|
|
7509
7653
|
const runtimes = await resolveRuntimes(flags);
|
|
7510
7654
|
if (runtimes === null) return;
|
|
7655
|
+
try {
|
|
7656
|
+
assertSelectedPromptFiles(runtimes, flags.targetDir);
|
|
7657
|
+
} catch (error) {
|
|
7658
|
+
p6.log.error(error instanceof Error ? error.message : String(error));
|
|
7659
|
+
process.exitCode = 1;
|
|
7660
|
+
return;
|
|
7661
|
+
}
|
|
7511
7662
|
const fileRuntimes = runtimes.filter(isFileManagedRuntime);
|
|
7512
7663
|
const preferenceFile = installModePreferenceFile();
|
|
7513
7664
|
const explicitMode = flags.mode !== void 0 || flags.subagentConcurrency !== void 0;
|