jorgex-stack 1.0.30 → 1.0.31
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/dist/cli.js +75 -123
- package/package.json +1 -1
- package/stack/agents/README.md +2 -2
- package/stack/agents/orchestrator.md +1 -221
- package/stack/agents/test-analyzer.md +10 -13
- package/stack/skills/agent-delegation/SKILL.md +3 -3
- package/stack/skills/orchestrator/SKILL.md +228 -0
- package/stack/modes/programmatic/agent-delegation.addendum.md +0 -11
package/dist/cli.js
CHANGED
|
@@ -5,7 +5,7 @@ import * as p6 from "@clack/prompts";
|
|
|
5
5
|
import { pathToFileURL as pathToFileURL2 } from "url";
|
|
6
6
|
|
|
7
7
|
// src/install.ts
|
|
8
|
-
import
|
|
8
|
+
import fs13 from "fs";
|
|
9
9
|
import path19 from "path";
|
|
10
10
|
import * as p from "@clack/prompts";
|
|
11
11
|
|
|
@@ -546,8 +546,6 @@ var opencodeAdapter = {
|
|
|
546
546
|
${deny}`);
|
|
547
547
|
}
|
|
548
548
|
if (!agent.spawn) lines.push(" task: deny");
|
|
549
|
-
lines.push("tools:");
|
|
550
|
-
lines.push(` write: ${agent.readonly ? "false" : "true"}`);
|
|
551
549
|
}
|
|
552
550
|
return [
|
|
553
551
|
{
|
|
@@ -803,16 +801,7 @@ description: ${yamlString2(agent.description)}
|
|
|
803
801
|
keep-coding-instructions: true
|
|
804
802
|
---
|
|
805
803
|
${agent.body}`;
|
|
806
|
-
|
|
807
|
-
const skill = `---
|
|
808
|
-
name: ${agent.name}
|
|
809
|
-
description: ${yamlString2(skillDescription)}
|
|
810
|
-
---
|
|
811
|
-
${agent.body}`;
|
|
812
|
-
return [
|
|
813
|
-
{ file: `${agent.name}.md`, content: style, kind: "output-style" },
|
|
814
|
-
{ file: `${agent.name}/SKILL.md`, content: skill, kind: "skill" }
|
|
815
|
-
];
|
|
804
|
+
return [{ file: `${agent.name}.md`, content: style, kind: "output-style" }];
|
|
816
805
|
}
|
|
817
806
|
const lines = [`name: ${agent.name}`, `description: ${yamlString2(agent.description)}`];
|
|
818
807
|
const tools = toolsFor(agent);
|
|
@@ -995,16 +984,7 @@ var codexAdapter = {
|
|
|
995
984
|
`# Uso: codex --profile ${agent.name}`,
|
|
996
985
|
`developer_instructions = ${tomlMultiline(agent.body)}`
|
|
997
986
|
];
|
|
998
|
-
|
|
999
|
-
const skill = `---
|
|
1000
|
-
name: ${agent.name}
|
|
1001
|
-
description: ${JSON.stringify(skillDescription)}
|
|
1002
|
-
---
|
|
1003
|
-
${agent.body}`;
|
|
1004
|
-
return [
|
|
1005
|
-
{ file: `${agent.name}.config.toml`, content: profileLines.join("\n") + "\n", kind: "profile" },
|
|
1006
|
-
{ file: `${agent.name}/SKILL.md`, content: skill, kind: "skill" }
|
|
1007
|
-
];
|
|
987
|
+
return [{ file: `${agent.name}.config.toml`, content: profileLines.join("\n") + "\n", kind: "profile" }];
|
|
1008
988
|
}
|
|
1009
989
|
const lines = [
|
|
1010
990
|
`name = ${tomlString(agent.name)}`,
|
|
@@ -1379,13 +1359,6 @@ import path13 from "path";
|
|
|
1379
1359
|
var PROGRAMMATIC_ROOT = ["modes", "programmatic"];
|
|
1380
1360
|
var PROGRAMMATIC_MARKER = "<!-- jorgex:programmatic-mode -->";
|
|
1381
1361
|
var LEGACY_RESULT_CONTRACT_SECTION = /\n?##\s+Result contract[\s\S]*$/;
|
|
1382
|
-
var LEGACY_SKILL_DELEGATION_SECTION = /\n?##\s+Formato obligatorio[\s\S]*$/;
|
|
1383
|
-
var LEGACY_DELEGATION_LINE = /- For each `→ \[agent\]: \.\.\.` line, launch the corresponding specialist\./;
|
|
1384
|
-
var LEGACY_PROGRAMMATIC_PHRASES = [
|
|
1385
|
-
[/\bResult contract\b/g, "strict JSON handoff"],
|
|
1386
|
-
[/Status \/ Delegations \/ Risks/g, "status, delegations, and risks"],
|
|
1387
|
-
[LEGACY_DELEGATION_LINE, "- Process the JSON `delegations[]` array and launch the corresponding specialist."]
|
|
1388
|
-
];
|
|
1389
1362
|
var normalize = (value) => value.replace(/\r\n/g, "\n");
|
|
1390
1363
|
function loadProgrammaticAddendum(stackDir, fileName) {
|
|
1391
1364
|
return normalize(fs9.readFileSync(path13.join(stackDir, ...PROGRAMMATIC_ROOT, fileName), "utf8")).trim();
|
|
@@ -1399,10 +1372,7 @@ ${addendum}
|
|
|
1399
1372
|
`;
|
|
1400
1373
|
}
|
|
1401
1374
|
function stripLegacyResultContract(body) {
|
|
1402
|
-
return
|
|
1403
|
-
(text2, [pattern, replacement]) => text2.replace(pattern, replacement),
|
|
1404
|
-
normalize(body).replace(LEGACY_RESULT_CONTRACT_SECTION, "")
|
|
1405
|
-
).trimEnd();
|
|
1375
|
+
return normalize(body).replace(LEGACY_RESULT_CONTRACT_SECTION, "").replace(/\bResult contract\b/g, "strict JSON handoff").trimEnd();
|
|
1406
1376
|
}
|
|
1407
1377
|
function concurrencyRule(concurrency) {
|
|
1408
1378
|
if (concurrency === "parallel") {
|
|
@@ -1422,22 +1392,13 @@ function composeProgrammaticSystemPrompt(stackDir, content, mode) {
|
|
|
1422
1392
|
}
|
|
1423
1393
|
function composeProgrammaticAgentBody(stackDir, agent, mode, concurrency) {
|
|
1424
1394
|
if (mode !== "programmatic") return normalize(agent.body);
|
|
1425
|
-
const fileName = agent.mode === "primary" ? "orchestrator.addendum.md" : "subagent.addendum.md";
|
|
1426
|
-
let addendum = loadProgrammaticAddendum(stackDir, fileName);
|
|
1427
1395
|
if (agent.mode === "primary") {
|
|
1428
|
-
|
|
1396
|
+
const addendum2 = loadProgrammaticAddendum(stackDir, "orchestrator.addendum.md").replace("{{CONCURRENCY_RULE}}", concurrencyRule(concurrency ?? "serial"));
|
|
1397
|
+
return appendAddendum(agent.body, addendum2);
|
|
1429
1398
|
}
|
|
1399
|
+
const addendum = loadProgrammaticAddendum(stackDir, "subagent.addendum.md");
|
|
1430
1400
|
return appendAddendum(stripLegacyResultContract(agent.body), addendum);
|
|
1431
1401
|
}
|
|
1432
|
-
function composeProgrammaticSkillBody(stackDir, skillPath, content, mode) {
|
|
1433
|
-
if (mode !== "programmatic") return normalize(content);
|
|
1434
|
-
if (skillPath !== path13.join("agent-delegation", "SKILL.md")) return normalize(content);
|
|
1435
|
-
const base = normalize(content).replace(
|
|
1436
|
-
"Las delegaciones van **en tu output final**, en el formato de abajo. El orquestador las lee y decide a qui\xE9n invocar.",
|
|
1437
|
-
"Las delegaciones van como strings en el JSON final `delegations[]`. El orquestador las lee y decide a qui\xE9n invocar."
|
|
1438
|
-
).replace(LEGACY_SKILL_DELEGATION_SECTION, "").trimEnd();
|
|
1439
|
-
return appendAddendum(base, loadProgrammaticAddendum(stackDir, "agent-delegation.addendum.md"));
|
|
1440
|
-
}
|
|
1441
1402
|
|
|
1442
1403
|
// src/components/system-prompt.ts
|
|
1443
1404
|
var normalize2 = (s) => s.replace(/\r\n/g, "\n");
|
|
@@ -1461,12 +1422,11 @@ function planSystemPrompt(adapter, ctx) {
|
|
|
1461
1422
|
// src/components/agents.ts
|
|
1462
1423
|
import path15 from "path";
|
|
1463
1424
|
function planAgents(adapter, ctx) {
|
|
1464
|
-
const { agentsDir, commandsDir, outputStylesDir,
|
|
1425
|
+
const { agentsDir, commandsDir, outputStylesDir, profilesDir, scriptsDir } = adapter.paths(ctx.configDir);
|
|
1465
1426
|
const dirFor = {
|
|
1466
1427
|
agent: agentsDir,
|
|
1467
1428
|
command: commandsDir,
|
|
1468
1429
|
"output-style": outputStylesDir,
|
|
1469
|
-
skill: skillsDir,
|
|
1470
1430
|
profile: profilesDir
|
|
1471
1431
|
};
|
|
1472
1432
|
const scriptsBase = scriptsDir.replace(/\\/g, "/");
|
|
@@ -1489,43 +1449,35 @@ function planAgents(adapter, ctx) {
|
|
|
1489
1449
|
|
|
1490
1450
|
// src/components/skills.ts
|
|
1491
1451
|
import path16 from "path";
|
|
1492
|
-
import fs11 from "fs";
|
|
1493
1452
|
function planSkills(adapter, ctx) {
|
|
1494
1453
|
const { skillsDir } = adapter.paths(ctx.configDir);
|
|
1495
1454
|
const source = path16.join(ctx.stackDir, "skills");
|
|
1496
1455
|
return listFilesRecursive(source).map((file) => {
|
|
1497
1456
|
const relative = path16.relative(source, file);
|
|
1498
|
-
if (relative === path16.join("agent-delegation", "SKILL.md") && ctx.mode === "programmatic") {
|
|
1499
|
-
return {
|
|
1500
|
-
kind: "write",
|
|
1501
|
-
target: path16.join(skillsDir, relative),
|
|
1502
|
-
content: composeProgrammaticSkillBody(ctx.stackDir, relative, fs11.readFileSync(file, "utf8"), ctx.mode)
|
|
1503
|
-
};
|
|
1504
|
-
}
|
|
1505
1457
|
return { kind: "copy", source: file, target: path16.join(skillsDir, relative) };
|
|
1506
1458
|
});
|
|
1507
1459
|
}
|
|
1508
1460
|
|
|
1509
1461
|
// src/components/commands.ts
|
|
1510
1462
|
import path17 from "path";
|
|
1511
|
-
import
|
|
1463
|
+
import fs11 from "fs";
|
|
1512
1464
|
function planCommands(adapter, ctx) {
|
|
1513
1465
|
const { commandsDir } = adapter.paths(ctx.configDir);
|
|
1514
1466
|
const source = path17.join(ctx.stackDir, "commands");
|
|
1515
|
-
if (!
|
|
1467
|
+
if (!fs11.existsSync(source)) return [];
|
|
1516
1468
|
const commandFiles = [
|
|
1517
1469
|
...listMarkdownFiles(source),
|
|
1518
1470
|
...listMarkdownFiles(path17.join(source, adapter.id))
|
|
1519
1471
|
];
|
|
1520
1472
|
return commandFiles.map(({ file, fullPath }) => {
|
|
1521
|
-
const raw =
|
|
1473
|
+
const raw = fs11.readFileSync(fullPath, "utf8").replace(/\r\n/g, "\n");
|
|
1522
1474
|
const rendered = adapter.renderCommand(file, raw);
|
|
1523
1475
|
return { kind: "write", target: path17.join(commandsDir, rendered.file), content: rendered.content };
|
|
1524
1476
|
});
|
|
1525
1477
|
}
|
|
1526
1478
|
function listMarkdownFiles(dir) {
|
|
1527
|
-
if (!
|
|
1528
|
-
return
|
|
1479
|
+
if (!fs11.existsSync(dir)) return [];
|
|
1480
|
+
return fs11.readdirSync(dir, { withFileTypes: true }).filter((entry) => entry.isFile() && entry.name.endsWith(".md")).map((entry) => ({ file: entry.name, fullPath: path17.join(dir, entry.name) }));
|
|
1529
1481
|
}
|
|
1530
1482
|
|
|
1531
1483
|
// src/components/hooks.ts
|
|
@@ -1540,22 +1492,22 @@ function planMcp(adapter, ctx) {
|
|
|
1540
1492
|
|
|
1541
1493
|
// src/components/plugins.ts
|
|
1542
1494
|
import path18 from "path";
|
|
1543
|
-
import
|
|
1495
|
+
import fs12 from "fs";
|
|
1544
1496
|
function planPlugins(adapter, ctx) {
|
|
1545
1497
|
const { pluginsDir } = adapter.paths(ctx.configDir);
|
|
1546
1498
|
if (pluginsDir === null) return [];
|
|
1547
1499
|
const source = path18.join(ctx.stackDir, "plugins", adapter.id);
|
|
1548
|
-
if (!
|
|
1500
|
+
if (!fs12.existsSync(source)) return [];
|
|
1549
1501
|
return listFilesRecursive(source).filter((f) => f.endsWith(".ts")).map((sourceFile) => {
|
|
1550
1502
|
const target = path18.join(pluginsDir, path18.relative(source, sourceFile));
|
|
1551
|
-
const raw =
|
|
1503
|
+
const raw = fs12.readFileSync(sourceFile, "utf8");
|
|
1552
1504
|
let content = raw;
|
|
1553
1505
|
if (content.includes('"{{ENGRAM_BIN}}"')) {
|
|
1554
1506
|
content = content.replace(/"\{\{ENGRAM_BIN\}\}"/g, JSON.stringify(ctx.engramBin ?? "engram"));
|
|
1555
1507
|
}
|
|
1556
1508
|
if (content.includes('"{{ENGRAM_PROTOCOL}}"')) {
|
|
1557
1509
|
const protocol = stripLeadingHtmlComments(
|
|
1558
|
-
|
|
1510
|
+
fs12.readFileSync(path18.join(ctx.stackDir, "system-prompt", "engram-protocol.md"), "utf8").replace(/\r\n/g, "\n")
|
|
1559
1511
|
);
|
|
1560
1512
|
content = content.replace(/"\{\{ENGRAM_PROTOCOL\}\}"/g, JSON.stringify(protocol));
|
|
1561
1513
|
}
|
|
@@ -1602,7 +1554,7 @@ function diffPlan(plan) {
|
|
|
1602
1554
|
if (current === null) return { action, status: "create" };
|
|
1603
1555
|
return { action, status: current === action.content ? "unchanged" : "update" };
|
|
1604
1556
|
}
|
|
1605
|
-
if (!
|
|
1557
|
+
if (!fs13.existsSync(action.target)) return { action, status: "create" };
|
|
1606
1558
|
return { action, status: sameFileContent(action.source, action.target) ? "unchanged" : "update" };
|
|
1607
1559
|
});
|
|
1608
1560
|
}
|
|
@@ -1734,7 +1686,7 @@ async function runInstall(opts) {
|
|
|
1734
1686
|
applyChanges(changes);
|
|
1735
1687
|
const pruneRoot = useManifest ? HOME : path19.dirname(configDir);
|
|
1736
1688
|
for (const orphan of orphans) {
|
|
1737
|
-
|
|
1689
|
+
fs13.rmSync(orphan, { force: true });
|
|
1738
1690
|
pruneEmptyDirs(orphan, pruneRoot);
|
|
1739
1691
|
}
|
|
1740
1692
|
const verifyCtx = { ...ctx, warnings: [] };
|
|
@@ -1757,7 +1709,7 @@ async function runInstall(opts) {
|
|
|
1757
1709
|
}
|
|
1758
1710
|
|
|
1759
1711
|
// src/uninstall.ts
|
|
1760
|
-
import
|
|
1712
|
+
import fs14 from "fs";
|
|
1761
1713
|
import path20 from "path";
|
|
1762
1714
|
import * as p2 from "@clack/prompts";
|
|
1763
1715
|
async function runUninstall(opts) {
|
|
@@ -1812,7 +1764,7 @@ async function runUninstall(opts) {
|
|
|
1812
1764
|
const pruneRoot = usingRealConfig ? HOME : path20.dirname(configDir);
|
|
1813
1765
|
const planTargets = [
|
|
1814
1766
|
.../* @__PURE__ */ new Set([...buildPlan(adapter, ctx).map((a) => path20.resolve(a.target)), ...prevOwned.map((t) => path20.resolve(t))])
|
|
1815
|
-
].filter((t) => !mergedTargets.has(t) &&
|
|
1767
|
+
].filter((t) => !mergedTargets.has(t) && fs14.existsSync(t));
|
|
1816
1768
|
const deleteTargets = planTargets.filter(
|
|
1817
1769
|
(t) => !retained.has(t) && !(ctx.preserveEngram && path20.basename(t) === "engram.ts") && isContainedIn(t, pruneRoot)
|
|
1818
1770
|
);
|
|
@@ -1822,18 +1774,18 @@ async function runUninstall(opts) {
|
|
|
1822
1774
|
if (sharedKept > 0) p2.log.info(`${sharedKept} archivos se conservan: otros runtimes instalados los siguen usando.`);
|
|
1823
1775
|
if (opts.dryRun) continue;
|
|
1824
1776
|
const backup = createBackup(
|
|
1825
|
-
[...deleteTargets, ...unmerge.map((a) => a.target).filter((t) =>
|
|
1777
|
+
[...deleteTargets, ...unmerge.map((a) => a.target).filter((t) => fs14.existsSync(t))],
|
|
1826
1778
|
`uninstall-${id}`
|
|
1827
1779
|
);
|
|
1828
1780
|
if (backup) p2.log.info(`Backup: ${backup.id} (${backup.files.length} archivos)`);
|
|
1829
1781
|
for (const target of deleteTargets) {
|
|
1830
|
-
|
|
1782
|
+
fs14.rmSync(target, { force: true });
|
|
1831
1783
|
pruneEmptyDirs(target, pruneRoot);
|
|
1832
1784
|
}
|
|
1833
1785
|
for (const action of unmerge) {
|
|
1834
1786
|
if (action.kind !== "write") continue;
|
|
1835
1787
|
if (action.content.trim() === "") {
|
|
1836
|
-
|
|
1788
|
+
fs14.rmSync(action.target, { force: true });
|
|
1837
1789
|
} else {
|
|
1838
1790
|
writeText(action.target, action.content);
|
|
1839
1791
|
}
|
|
@@ -1847,7 +1799,7 @@ async function runUninstall(opts) {
|
|
|
1847
1799
|
|
|
1848
1800
|
// src/doctor.ts
|
|
1849
1801
|
import path21 from "path";
|
|
1850
|
-
import
|
|
1802
|
+
import fs15 from "fs";
|
|
1851
1803
|
import * as p3 from "@clack/prompts";
|
|
1852
1804
|
function engramVersion(bin) {
|
|
1853
1805
|
const out = runDetectedBin(bin, ["--version"], 5e3);
|
|
@@ -1880,11 +1832,11 @@ async function runDoctor() {
|
|
|
1880
1832
|
}
|
|
1881
1833
|
const engramDataDir = process.env.ENGRAM_DATA_DIR ?? path21.join(HOME, ".engram");
|
|
1882
1834
|
const engramDb = path21.join(engramDataDir, "engram.db");
|
|
1883
|
-
if (
|
|
1884
|
-
const sizeMb = (
|
|
1835
|
+
if (fs15.existsSync(engramDb)) {
|
|
1836
|
+
const sizeMb = (fs15.statSync(engramDb).size / 1024 / 1024).toFixed(1);
|
|
1885
1837
|
p3.log.info(`Engram DB: ${engramDb} (${sizeMb} MB de memorias \u2014 el stack no la toca JAM\xC1S).`);
|
|
1886
1838
|
}
|
|
1887
|
-
if (!
|
|
1839
|
+
if (!fs15.existsSync(modelMapFile())) p3.log.info("model-map: a\xFAn no creado (se crea en el primer install o con 'models').");
|
|
1888
1840
|
const manifest = readManifest();
|
|
1889
1841
|
const modePreference = loadInstallModePreference();
|
|
1890
1842
|
const current = collectAllCurrentTargets(modePreference);
|
|
@@ -1923,10 +1875,10 @@ async function runDoctor() {
|
|
|
1923
1875
|
p3.log.warn(`${adapter.name}: ${orphans.length} archivos hu\xE9rfanos de versiones previas \u2192 ejecuta 'sync'.`);
|
|
1924
1876
|
problems++;
|
|
1925
1877
|
}
|
|
1926
|
-
if (adapter.id === "codex" &&
|
|
1878
|
+
if (adapter.id === "codex" && fs15.existsSync(path21.join(detection.configDir, "hooks.json"))) {
|
|
1927
1879
|
p3.log.info("Codex: recuerda que los hooks requieren aprobaci\xF3n manual \u2014 verifica con /hooks dentro de codex.");
|
|
1928
1880
|
}
|
|
1929
|
-
if (adapter.id === "codex" &&
|
|
1881
|
+
if (adapter.id === "codex" && fs15.existsSync(path21.join(detection.configDir, "AGENTS.override.md"))) {
|
|
1930
1882
|
p3.log.warn(
|
|
1931
1883
|
"Codex: existe ~/.codex/AGENTS.override.md \u2014 tiene prioridad ABSOLUTA y tapa el AGENTS.md gestionado por el stack."
|
|
1932
1884
|
);
|
|
@@ -1940,14 +1892,14 @@ async function runDoctor() {
|
|
|
1940
1892
|
}
|
|
1941
1893
|
|
|
1942
1894
|
// src/update.ts
|
|
1943
|
-
import
|
|
1895
|
+
import fs18 from "fs";
|
|
1944
1896
|
import path24 from "path";
|
|
1945
1897
|
import os3 from "os";
|
|
1946
1898
|
import { execFileSync as execFileSync4 } from "child_process";
|
|
1947
1899
|
import * as p4 from "@clack/prompts";
|
|
1948
1900
|
|
|
1949
1901
|
// src/lib/github.ts
|
|
1950
|
-
import
|
|
1902
|
+
import fs16 from "fs";
|
|
1951
1903
|
import path22 from "path";
|
|
1952
1904
|
import { execFileSync as execFileSync2 } from "child_process";
|
|
1953
1905
|
import os2 from "os";
|
|
@@ -2022,7 +1974,7 @@ function validateExtractedTree(destDir) {
|
|
|
2022
1974
|
const walk = (dir) => {
|
|
2023
1975
|
let entries;
|
|
2024
1976
|
try {
|
|
2025
|
-
entries =
|
|
1977
|
+
entries = fs16.readdirSync(dir, { withFileTypes: true });
|
|
2026
1978
|
} catch {
|
|
2027
1979
|
return false;
|
|
2028
1980
|
}
|
|
@@ -2030,7 +1982,7 @@ function validateExtractedTree(destDir) {
|
|
|
2030
1982
|
const full = path22.join(dir, entry.name);
|
|
2031
1983
|
let stat;
|
|
2032
1984
|
try {
|
|
2033
|
-
stat =
|
|
1985
|
+
stat = fs16.lstatSync(full);
|
|
2034
1986
|
} catch {
|
|
2035
1987
|
return false;
|
|
2036
1988
|
}
|
|
@@ -2047,14 +1999,14 @@ function validateExtractedTree(destDir) {
|
|
|
2047
1999
|
function resolveTarBin() {
|
|
2048
2000
|
if (process.platform !== "win32") return "tar";
|
|
2049
2001
|
const winTar = path22.join(process.env["SystemRoot"] ?? "C:\\Windows", "System32", "tar.exe");
|
|
2050
|
-
return
|
|
2002
|
+
return fs16.existsSync(winTar) ? winTar : "tar";
|
|
2051
2003
|
}
|
|
2052
2004
|
async function downloadRepoTarball(repo, sha, destDir, validateSubdir) {
|
|
2053
2005
|
const url = `https://codeload.github.com/${repo}/tar.gz/${sha}`;
|
|
2054
2006
|
const tmp = path22.join(os2.tmpdir(), `jorgex-tarball-${Date.now()}.tar.gz`);
|
|
2055
2007
|
const fail = (reason) => {
|
|
2056
2008
|
try {
|
|
2057
|
-
|
|
2009
|
+
fs16.rmSync(destDir, { recursive: true, force: true });
|
|
2058
2010
|
} catch {
|
|
2059
2011
|
}
|
|
2060
2012
|
return { ok: false, reason };
|
|
@@ -2073,10 +2025,10 @@ async function downloadRepoTarball(repo, sha, destDir, validateSubdir) {
|
|
|
2073
2025
|
if (!res.body) return fail("respuesta HTTP sin cuerpo");
|
|
2074
2026
|
await pipeline(
|
|
2075
2027
|
Readable.fromWeb(res.body),
|
|
2076
|
-
|
|
2028
|
+
fs16.createWriteStream(tmp)
|
|
2077
2029
|
);
|
|
2078
|
-
|
|
2079
|
-
|
|
2030
|
+
fs16.rmSync(destDir, { recursive: true, force: true });
|
|
2031
|
+
fs16.mkdirSync(destDir, { recursive: true });
|
|
2080
2032
|
try {
|
|
2081
2033
|
execFileSync2(resolveTarBin(), ["-xzf", tmp, "--strip-components=1", "-C", destDir], { stdio: "pipe" });
|
|
2082
2034
|
} catch (err) {
|
|
@@ -2089,30 +2041,30 @@ async function downloadRepoTarball(repo, sha, destDir, validateSubdir) {
|
|
|
2089
2041
|
if (validateRoot !== resolvedDest && !isContainedIn(validateRoot, resolvedDest)) {
|
|
2090
2042
|
return fail(`la ruta de validaci\xF3n "${validateSubdir}" escapa del destino`);
|
|
2091
2043
|
}
|
|
2092
|
-
if (
|
|
2044
|
+
if (fs16.existsSync(validateRoot) && !validateExtractedTree(validateRoot)) {
|
|
2093
2045
|
return fail("el \xE1rbol extra\xEDdo contiene symlinks o rutas fuera del destino");
|
|
2094
2046
|
}
|
|
2095
|
-
const validated =
|
|
2047
|
+
const validated = fs16.existsSync(validateRoot);
|
|
2096
2048
|
return { ok: true, validated };
|
|
2097
2049
|
} catch (err) {
|
|
2098
2050
|
const timedOut = err instanceof Error && (err.name === "TimeoutError" || err.name === "AbortError");
|
|
2099
2051
|
return fail(timedOut ? "timeout de descarga (120s)" : err instanceof Error ? `fallo de red: ${err.message}` : "error desconocido");
|
|
2100
2052
|
} finally {
|
|
2101
2053
|
try {
|
|
2102
|
-
|
|
2054
|
+
fs16.rmSync(tmp, { force: true });
|
|
2103
2055
|
} catch {
|
|
2104
2056
|
}
|
|
2105
2057
|
}
|
|
2106
2058
|
}
|
|
2107
2059
|
|
|
2108
2060
|
// src/lib/skill-update.ts
|
|
2109
|
-
import
|
|
2061
|
+
import fs17 from "fs";
|
|
2110
2062
|
import path23 from "path";
|
|
2111
2063
|
import { execFileSync as execFileSync3 } from "child_process";
|
|
2112
2064
|
var PROTECTED_SKILLS = /* @__PURE__ */ new Set(["agent-delegation", "work-lifecycle", "xreview"]);
|
|
2113
2065
|
function sameTextContentNormalized(a, b) {
|
|
2114
|
-
const ba =
|
|
2115
|
-
const bb =
|
|
2066
|
+
const ba = fs17.readFileSync(a);
|
|
2067
|
+
const bb = fs17.readFileSync(b);
|
|
2116
2068
|
if (ba.equals(bb)) return true;
|
|
2117
2069
|
const sa = ba.toString("utf8").replace(/\r\n/g, "\n");
|
|
2118
2070
|
const sb = bb.toString("utf8").replace(/\r\n/g, "\n");
|
|
@@ -2194,7 +2146,7 @@ function replaceSkill(name, upstreamSkillDir, newCommit, opts) {
|
|
|
2194
2146
|
throw new Error(`La skill "${name}" es propia del stack y no se actualiza desde upstream.`);
|
|
2195
2147
|
}
|
|
2196
2148
|
const upstreamsFile = upstreamsFilePath ?? path23.join(path23.dirname(stackRoot()), "upstreams.json");
|
|
2197
|
-
const raw =
|
|
2149
|
+
const raw = fs17.readFileSync(upstreamsFile, "utf8");
|
|
2198
2150
|
const data = JSON.parse(raw);
|
|
2199
2151
|
const skillEntry = data?.skills?.[name];
|
|
2200
2152
|
if (!skillEntry) {
|
|
@@ -2213,7 +2165,7 @@ function replaceSkill(name, upstreamSkillDir, newCommit, opts) {
|
|
|
2213
2165
|
try {
|
|
2214
2166
|
const upstreamFiles = listFilesRecursive(upstreamSkillDir);
|
|
2215
2167
|
for (const src of upstreamFiles) {
|
|
2216
|
-
const st =
|
|
2168
|
+
const st = fs17.lstatSync(src);
|
|
2217
2169
|
if (st.isSymbolicLink()) {
|
|
2218
2170
|
throw new Error(`Symlink rechazado en upstream de skill "${name}": ${src}`);
|
|
2219
2171
|
}
|
|
@@ -2223,14 +2175,14 @@ function replaceSkill(name, upstreamSkillDir, newCommit, opts) {
|
|
|
2223
2175
|
copyFile(src, dest);
|
|
2224
2176
|
}
|
|
2225
2177
|
const oldDir = `${localSkillDir}.old-${process.pid}`;
|
|
2226
|
-
if (
|
|
2227
|
-
|
|
2178
|
+
if (fs17.existsSync(localSkillDir)) {
|
|
2179
|
+
fs17.renameSync(localSkillDir, oldDir);
|
|
2228
2180
|
}
|
|
2229
|
-
|
|
2230
|
-
|
|
2181
|
+
fs17.renameSync(stagingDir, localSkillDir);
|
|
2182
|
+
fs17.rmSync(oldDir, { recursive: true, force: true });
|
|
2231
2183
|
} catch (err) {
|
|
2232
2184
|
try {
|
|
2233
|
-
|
|
2185
|
+
fs17.rmSync(stagingDir, { recursive: true, force: true });
|
|
2234
2186
|
} catch {
|
|
2235
2187
|
}
|
|
2236
2188
|
throw err;
|
|
@@ -2245,7 +2197,7 @@ function rateLimitHint(prefix) {
|
|
|
2245
2197
|
}
|
|
2246
2198
|
function loadUpstreams() {
|
|
2247
2199
|
const file = path24.join(path24.dirname(stackRoot()), "upstreams.json");
|
|
2248
|
-
return JSON.parse(
|
|
2200
|
+
return JSON.parse(fs18.readFileSync(file, "utf8"));
|
|
2249
2201
|
}
|
|
2250
2202
|
function skillsToScan(maintainer, upstreams) {
|
|
2251
2203
|
return maintainer ? Object.keys(upstreams.skills) : [];
|
|
@@ -2358,7 +2310,7 @@ function isEngramRunning() {
|
|
|
2358
2310
|
}
|
|
2359
2311
|
}
|
|
2360
2312
|
function isGitClone(projectRoot = path24.dirname(stackRoot())) {
|
|
2361
|
-
return
|
|
2313
|
+
return fs18.existsSync(path24.join(projectRoot, ".git"));
|
|
2362
2314
|
}
|
|
2363
2315
|
var STACK_METHOD_CLONE = "git pull + pnpm install + pnpm build";
|
|
2364
2316
|
function resolvePnpm() {
|
|
@@ -2368,7 +2320,7 @@ function resolvePnpm() {
|
|
|
2368
2320
|
}
|
|
2369
2321
|
function cleanupTmp(dir) {
|
|
2370
2322
|
try {
|
|
2371
|
-
|
|
2323
|
+
fs18.rmSync(dir, { recursive: true, force: true });
|
|
2372
2324
|
} catch {
|
|
2373
2325
|
}
|
|
2374
2326
|
}
|
|
@@ -2390,7 +2342,7 @@ function updateStackGlobal() {
|
|
|
2390
2342
|
execFileSync4(pnpm, ["add", "-g", "jorgex-stack@latest"], { stdio: "inherit" });
|
|
2391
2343
|
}
|
|
2392
2344
|
async function downloadSkillToTemp(repo, sha, skillPath) {
|
|
2393
|
-
const root =
|
|
2345
|
+
const root = fs18.mkdtempSync(path24.join(os3.tmpdir(), "jorgex-skill-"));
|
|
2394
2346
|
try {
|
|
2395
2347
|
const result = await downloadRepoTarball(repo, sha, root, skillPath);
|
|
2396
2348
|
if (!result.ok) {
|
|
@@ -2403,10 +2355,10 @@ async function downloadSkillToTemp(repo, sha, skillPath) {
|
|
|
2403
2355
|
cleanupTmp(root);
|
|
2404
2356
|
return { error: `la ruta "${skillPath}" escapa del directorio temporal` };
|
|
2405
2357
|
}
|
|
2406
|
-
if (
|
|
2358
|
+
if (fs18.existsSync(sub)) return { dir: sub, root };
|
|
2407
2359
|
const lastSeg = skillPath.split("/").pop();
|
|
2408
2360
|
const sub2 = path24.resolve(path24.join(root, lastSeg));
|
|
2409
|
-
if (isContainedIn(sub2, root) &&
|
|
2361
|
+
if (isContainedIn(sub2, root) && fs18.existsSync(sub2)) {
|
|
2410
2362
|
if (!validateExtractedTree(sub2)) {
|
|
2411
2363
|
cleanupTmp(root);
|
|
2412
2364
|
return { error: `el sub\xE1rbol "${lastSeg}" contiene symlinks o rutas fuera del destino` };
|
|
@@ -2425,11 +2377,11 @@ async function downloadSkillToTemp(repo, sha, skillPath) {
|
|
|
2425
2377
|
function pruneEngramDbBackups() {
|
|
2426
2378
|
try {
|
|
2427
2379
|
const dir = dataDir();
|
|
2428
|
-
if (!
|
|
2429
|
-
const backups =
|
|
2380
|
+
if (!fs18.existsSync(dir)) return;
|
|
2381
|
+
const backups = fs18.readdirSync(dir).filter((f) => f.startsWith("engram-db-backup-") && f.endsWith(".db")).map((f) => ({ name: f, mtime: fs18.statSync(path24.join(dir, f)).mtimeMs })).sort((a, b) => b.mtime - a.mtime);
|
|
2430
2382
|
for (const old of backups.slice(3)) {
|
|
2431
2383
|
try {
|
|
2432
|
-
|
|
2384
|
+
fs18.rmSync(path24.join(dir, old.name));
|
|
2433
2385
|
} catch {
|
|
2434
2386
|
}
|
|
2435
2387
|
}
|
|
@@ -2437,7 +2389,7 @@ function pruneEngramDbBackups() {
|
|
|
2437
2389
|
}
|
|
2438
2390
|
}
|
|
2439
2391
|
function rotateLockedBinary(binPath, sweepRoot = HOME) {
|
|
2440
|
-
if (!
|
|
2392
|
+
if (!fs18.existsSync(binPath)) return null;
|
|
2441
2393
|
const dir = path24.dirname(binPath);
|
|
2442
2394
|
const base = path24.basename(binPath);
|
|
2443
2395
|
const escapedBase = base.replace(/[.*+?^$()|[\]{}\\]/g, "\\$&");
|
|
@@ -2445,10 +2397,10 @@ function rotateLockedBinary(binPath, sweepRoot = HOME) {
|
|
|
2445
2397
|
const resolvedDir = path24.resolve(dir);
|
|
2446
2398
|
if (resolvedDir === path24.resolve(sweepRoot) || isContainedIn(resolvedDir, sweepRoot)) {
|
|
2447
2399
|
try {
|
|
2448
|
-
for (const entry of
|
|
2400
|
+
for (const entry of fs18.readdirSync(dir)) {
|
|
2449
2401
|
if (oldPattern.test(entry)) {
|
|
2450
2402
|
try {
|
|
2451
|
-
|
|
2403
|
+
fs18.rmSync(path24.join(dir, entry), { force: true });
|
|
2452
2404
|
} catch {
|
|
2453
2405
|
}
|
|
2454
2406
|
}
|
|
@@ -2457,7 +2409,7 @@ function rotateLockedBinary(binPath, sweepRoot = HOME) {
|
|
|
2457
2409
|
}
|
|
2458
2410
|
}
|
|
2459
2411
|
const rotated = path24.join(dir, `${base}.old-${Date.now()}`);
|
|
2460
|
-
|
|
2412
|
+
fs18.renameSync(binPath, rotated);
|
|
2461
2413
|
return rotated;
|
|
2462
2414
|
}
|
|
2463
2415
|
async function updateEngram(engramRepo, latestVersion) {
|
|
@@ -2468,7 +2420,7 @@ async function updateEngram(engramRepo, latestVersion) {
|
|
|
2468
2420
|
}
|
|
2469
2421
|
const engramDataDir = process.env.ENGRAM_DATA_DIR ?? path24.join(HOME, ".engram");
|
|
2470
2422
|
const engramDb = path24.join(engramDataDir, "engram.db");
|
|
2471
|
-
if (
|
|
2423
|
+
if (fs18.existsSync(engramDb)) {
|
|
2472
2424
|
const doBackup = await p4.confirm({
|
|
2473
2425
|
message: `\xBFHacer backup de la DB de Engram antes de actualizar? (${engramDb})`,
|
|
2474
2426
|
initialValue: true
|
|
@@ -2477,8 +2429,8 @@ async function updateEngram(engramRepo, latestVersion) {
|
|
|
2477
2429
|
const ts = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
2478
2430
|
const dest = path24.join(dataDir(), `engram-db-backup-${ts}.db`);
|
|
2479
2431
|
try {
|
|
2480
|
-
if (!
|
|
2481
|
-
|
|
2432
|
+
if (!fs18.existsSync(dataDir())) fs18.mkdirSync(dataDir(), { recursive: true });
|
|
2433
|
+
fs18.copyFileSync(engramDb, dest);
|
|
2482
2434
|
p4.log.success(`DB respaldada en ${dest} (la DB original NO se modifica jam\xE1s).`);
|
|
2483
2435
|
pruneEngramDbBackups();
|
|
2484
2436
|
} catch (err) {
|
|
@@ -2528,10 +2480,10 @@ async function updateEngram(engramRepo, latestVersion) {
|
|
|
2528
2480
|
["install", `github.com/Gentleman-Programming/engram/cmd/engram@v${latestVersion}`],
|
|
2529
2481
|
{ stdio: "inherit" }
|
|
2530
2482
|
);
|
|
2531
|
-
const rollbackOk = resolveEngramRollback({ installOk: true, rotated, bin, binExists:
|
|
2483
|
+
const rollbackOk = resolveEngramRollback({ installOk: true, rotated, bin, binExists: fs18.existsSync(bin ?? "") });
|
|
2532
2484
|
if (rollbackOk.action === "restore") {
|
|
2533
2485
|
try {
|
|
2534
|
-
|
|
2486
|
+
fs18.renameSync(rotated, bin);
|
|
2535
2487
|
p4.log.warn(rollbackOk.messages.onRestore);
|
|
2536
2488
|
} catch {
|
|
2537
2489
|
p4.log.warn(rollbackOk.messages.onRenameFail);
|
|
@@ -2539,10 +2491,10 @@ async function updateEngram(engramRepo, latestVersion) {
|
|
|
2539
2491
|
}
|
|
2540
2492
|
return true;
|
|
2541
2493
|
} catch (err) {
|
|
2542
|
-
const rollbackFail = resolveEngramRollback({ installOk: false, rotated, bin, binExists:
|
|
2494
|
+
const rollbackFail = resolveEngramRollback({ installOk: false, rotated, bin, binExists: fs18.existsSync(bin ?? "") });
|
|
2543
2495
|
if (rollbackFail.action === "restore") {
|
|
2544
2496
|
try {
|
|
2545
|
-
|
|
2497
|
+
fs18.renameSync(rotated, bin);
|
|
2546
2498
|
p4.log.info(rollbackFail.messages.onRestore);
|
|
2547
2499
|
} catch {
|
|
2548
2500
|
p4.log.error(rollbackFail.messages.onRenameFail);
|
|
@@ -3063,7 +3015,7 @@ function cancelled() {
|
|
|
3063
3015
|
}
|
|
3064
3016
|
|
|
3065
3017
|
// src/lib/release.ts
|
|
3066
|
-
import
|
|
3018
|
+
import fs19 from "fs";
|
|
3067
3019
|
import path26 from "path";
|
|
3068
3020
|
import { execFileSync as execFileSync5 } from "child_process";
|
|
3069
3021
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
@@ -3071,7 +3023,7 @@ function findPackageJson() {
|
|
|
3071
3023
|
let dir = path26.dirname(fileURLToPath2(import.meta.url));
|
|
3072
3024
|
for (let i = 0; i < 6; i++) {
|
|
3073
3025
|
const candidate = path26.join(dir, "package.json");
|
|
3074
|
-
if (
|
|
3026
|
+
if (fs19.existsSync(candidate)) return candidate;
|
|
3075
3027
|
dir = path26.dirname(dir);
|
|
3076
3028
|
}
|
|
3077
3029
|
throw new Error("No se encontr\xF3 package.json cerca del CLI.");
|
|
@@ -3081,7 +3033,7 @@ function readPackageVersion() {
|
|
|
3081
3033
|
}
|
|
3082
3034
|
function readPackageMetadata() {
|
|
3083
3035
|
const packageJson = findPackageJson();
|
|
3084
|
-
const raw =
|
|
3036
|
+
const raw = fs19.readFileSync(packageJson, "utf8");
|
|
3085
3037
|
const parsed = JSON.parse(raw);
|
|
3086
3038
|
const name = typeof parsed.name === "string" ? parsed.name.trim() : "";
|
|
3087
3039
|
const version = typeof parsed.version === "string" ? parsed.version.trim() : "";
|
package/package.json
CHANGED
package/stack/agents/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Agentes canónicos
|
|
2
2
|
|
|
3
|
-
Una sola fuente por agente. El instalador los traduce al formato de cada runtime (PRD §6): Markdown+frontmatter para Claude Code y OpenCode, TOML para Codex.
|
|
3
|
+
Una sola fuente por agente. El instalador los traduce al formato de cada runtime (PRD §6): Markdown+frontmatter para Claude Code y OpenCode, TOML para Codex. El workflow completo del orchestrator vive únicamente en `skills/orchestrator/SKILL.md`; el agente primary es un wrapper corto que obliga a cargar esa skill.
|
|
4
4
|
|
|
5
5
|
## Frontmatter canónico
|
|
6
6
|
|
|
@@ -19,7 +19,7 @@ Una sola fuente por agente. El instalador los traduce al formato de cada runtime
|
|
|
19
19
|
| Canónico | Claude Code | Codex | OpenCode |
|
|
20
20
|
|---|---|---|---|
|
|
21
21
|
| `tier` | alias `fable`/`opus`/`sonnet`/`haiku` según model-map | `model` + `model_reasoning_effort` | `provider/model` del model-map |
|
|
22
|
-
| `readonly: true` | `tools: Read, Grep, Glob` (+Bash si aplica) | `sandbox_mode = "read-only"` | `
|
|
22
|
+
| `readonly: true` | `tools: Read, Grep, Glob` (+Bash si aplica) | `sandbox_mode = "read-only"` | `permission: { edit: deny }` |
|
|
23
23
|
| `bash: git-read` | `Bash` completo en tools — la restricción a git read es solo de prompt (el frontmatter de Claude Code no tiene esa granularidad) | con `readonly: true` el sandbox read-only impide escrituras; con `readonly: false` la restricción es solo de prompt | `permission.bash: { "git diff*": allow, "git log*": allow }` (única aplicación real) |
|
|
24
24
|
| `bash: none` | sin `Bash` en tools | sandbox read-only | `permission: { bash: deny }` |
|
|
25
25
|
| `spawn: false` | sin tool `Agent`/`Task` | n/a | `permission: { task: deny }` |
|
|
@@ -9,224 +9,4 @@ bash: full
|
|
|
9
9
|
|
|
10
10
|
# Orchestrator
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
## Phases
|
|
15
|
-
|
|
16
|
-
```text
|
|
17
|
-
INIT → EXPLORE → SPEC → PLAN → EXECUTE → VERIFY → SHIP → CLOSE
|
|
18
|
-
```
|
|
19
|
-
|
|
20
|
-
### Autonomy
|
|
21
|
-
|
|
22
|
-
The human drives the flow UP TO the plan: the idea, the PRD review and the plan review are interactive. Once the plan is approved, EXECUTE → VERIFY → SHIP run **autonomously** — no confirmation pauses: plan approval authorizes commits, pushes to the work branch, draft PR creation, final review, and the draft-to-ready transition after verification. Task-critical uncertainty from a subagent is an operational blocker, not a pause in autonomy: answer from existing context first; only if the decision genuinely cannot be made from available context may you ask the user, then relaunch with explicit guidance. Control returns to the user at CLOSE. Merging the PR is NEVER yours: it always requires an explicit user order. For multi-PR work, each merge is a checkpoint; keep `work/{name}/PRD.md` and `plan.md` alive until the roadmap is finished. Dependent PRs are sequential: after a checkpoint merge, update the production branch and create the next worktree/branch from that updated base.
|
|
23
|
-
|
|
24
|
-
## 1. INIT
|
|
25
|
-
|
|
26
|
-
- Load previous context from Engram memory: for non-trivial reads, delegate to the `engram` subagent (`mem_context` / `mem_search` filtered to the task).
|
|
27
|
-
- Identify the project's constraints.
|
|
28
|
-
- Detect whether there is documentation, issues or artifacts already created.
|
|
29
|
-
|
|
30
|
-
## 2. EXPLORE
|
|
31
|
-
|
|
32
|
-
Launch analysts according to scope:
|
|
33
|
-
|
|
34
|
-
- `backend-analyst` if it affects backend, DB, APIs or server functions
|
|
35
|
-
- `frontend-analyst` if it affects UI, hooks, state or rendering
|
|
36
|
-
- `security-auditor` if the area is sensitive
|
|
37
|
-
|
|
38
|
-
## Base rule
|
|
39
|
-
|
|
40
|
-
- Your priority is to delegate.
|
|
41
|
-
- If a task has a clear subagent scope, delegate.
|
|
42
|
-
- If previous context is needed, gather context or analyze before deciding implementation.
|
|
43
|
-
|
|
44
|
-
### Delegation triggers
|
|
45
|
-
|
|
46
|
-
Once a task crosses any of these thresholds, delegating stops being optional:
|
|
47
|
-
|
|
48
|
-
| Trigger | Expected behavior |
|
|
49
|
-
| --- | --- |
|
|
50
|
-
| Reading 4+ files just to understand a flow | Delegate exploration to the matching analyst. |
|
|
51
|
-
| Wrong cwd, git/worktree accident, confusing test or env failure | Stop; re-explore with fresh context before continuing. |
|
|
52
|
-
| Long session with accumulating complexity | Pause and re-plan or delegate — or state explicitly why not. |
|
|
53
|
-
|
|
54
|
-
The goal is not ceremony: it is one responsible coordinator, one writer per scope, deterministic feedback while the diff is evolving, and fresh eyes at the PR boundary.
|
|
55
|
-
|
|
56
|
-
## 3. SPEC
|
|
57
|
-
|
|
58
|
-
- Synthesize findings.
|
|
59
|
-
- Propose a simple approach.
|
|
60
|
-
- Clarify only the real ambiguities.
|
|
61
|
-
- Apply the `lean-code` skill as a scope gate for any code-bearing task: ask whether the code is needed at all, whether stdlib/native/project helpers already solve it, and whether the smallest obvious change is enough.
|
|
62
|
-
- Backlog items phrased as "consider/evaluate X" are questions, not requirements: answer them HERE — who consumes it, what real case needs it — before they enter the PRD as committed scope. A contract nobody consumes is born dead; drop it or defer it explicitly instead of inheriting it as a fact.
|
|
63
|
-
- Create the PRD before moving to PLAN (see PRD rules).
|
|
64
|
-
|
|
65
|
-
### PRD rules
|
|
66
|
-
|
|
67
|
-
The PRD is **mandatory by default** when you work as orchestrator. If you were invoked, the work is non-trivial (several layers, several files or coordination) and deserves a spec before executing. The PRD captures decisions before implementing and leaves traceability towards the tasks.
|
|
68
|
-
|
|
69
|
-
Use the `to-prd` skill to turn the current context into the PRD (`work/{name}/PRD.md`) before planning execution.
|
|
70
|
-
|
|
71
|
-
**Escape valve (measurable)**: skip the PRD only if one of these applies:
|
|
72
|
-
|
|
73
|
-
- the user explicitly asks to skip it, or
|
|
74
|
-
- ALL of these hold: the change touches ≤ 3 files, AND stays in a single layer (only backend, only frontend, only docs…), AND changes no public contract (API, schema, exported types consumed elsewhere). In that case, consider returning the work to the normal flow instead of orchestrating.
|
|
75
|
-
|
|
76
|
-
If you skip it, say so explicitly and state which condition applied.
|
|
77
|
-
|
|
78
|
-
When presenting the PRD for review, offer a disposable HTML view (rules in the `work-lifecycle` skill).
|
|
79
|
-
|
|
80
|
-
If the work is large enough to benefit from explicit vertical slices, use the `to-issues` skill after the PRD to split it into independently executable slices before detailed planning.
|
|
81
|
-
|
|
82
|
-
## 4. PLAN
|
|
83
|
-
|
|
84
|
-
- Use the PRD as the base input for planning (it normally exists; only absent if the escape valve was used).
|
|
85
|
-
- If a slice breakdown exists from `to-issues`, use it as the structure for planning and task sequencing.
|
|
86
|
-
- Divide the work into clear tasks.
|
|
87
|
-
- One task = one agent = one scope.
|
|
88
|
-
- For tasks that add or grow code, record the lean-code outcome in the task spec/acceptance criteria so implementer and simplifier apply the same ladder.
|
|
89
|
-
- The PRD does not replace the plan or task breakdown: the PRD captures decisions; the plan and tasks turn those decisions into executable work.
|
|
90
|
-
- Materialize the plan per the Work state rules: `work/{name}/plan.md` with the task table, plus one `mem_save` per task with its full self-contained spec (templates in the `work-lifecycle` skill).
|
|
91
|
-
- When presenting the plan for review, offer a disposable HTML view (rules in the `work-lifecycle` skill). Requested changes go to plan.md; delete the HTML once the plan is approved, before EXECUTE.
|
|
92
|
-
|
|
93
|
-
## Work state
|
|
94
|
-
|
|
95
|
-
The `work-lifecycle` skill is the single source of this flow. Summary — every piece has exactly ONE home:
|
|
96
|
-
|
|
97
|
-
- `work/{name}/` (gitignored, exists only while the work is in progress) holds the human-reviewed artifacts: `PRD.md` and `plan.md`. They stay resident across intermediate PR merges; `plan.md` is the ONLY task status board — flip statuses with surgical edits; don't re-read the whole plan after every task (re-read it on resume).
|
|
98
|
-
- The full spec of each atomic task → Engram, one `mem_save` per task under `work/{name}/task/{NN}`. When you delegate a task, pass the subagent its topic_key + title — never the task content inline; it retrieves the spec itself.
|
|
99
|
-
- Phase outcomes, decisions and PR checkpoints → Engram under `work/{name}/{phase}` and `work/{name}/pr/{NN}`; tell each subagent which topic_key to use for its saves.
|
|
100
|
-
- Pending work → the project's single `work/backlog` topic_key, or issues (`to-issues`) if the project uses a tracker. Never a TODOs folder. For Engram, you are the **single writer**: before every change, retrieve the exact observation with `mem_get_observation`, preserve unrelated entries, send the complete content with `mem_update`, then read it again to verify. Never write it concurrently or use a blind topic-key upsert. Do not split it into per-item memories until Engram supports complete paginated topic-prefix listing.
|
|
101
|
-
- On final close: `mem_save` the outcome under `work/{name}/done`, move the PRD to the project's docs only if it has lasting documentation value, then delete `work/{name}/`. `work/{name}/done` is only for the last PR / final outcome. History is memory + git.
|
|
102
|
-
|
|
103
|
-
## Delegation map
|
|
104
|
-
|
|
105
|
-
Load the `agent-delegation` skill: it defines the available subagents, the scope of each and when to delegate. It is the single source of the agent map — don't duplicate the list here.
|
|
106
|
-
|
|
107
|
-
Every subagent ends with a **Result contract** (Status / Delegations / Risks). Process it:
|
|
108
|
-
|
|
109
|
-
- For each `→ [agent]: ...` line, launch the corresponding specialist.
|
|
110
|
-
- A delegation is unfinished work in another scope, not a request to append a generic quality pipeline. Normal handoffs between `implementer` and `tester` do not by themselves justify reviewers or analyzers.
|
|
111
|
-
- If a subagent reports `partial`, keep the safe work and relaunch only what still needs guidance.
|
|
112
|
-
- If a subagent reports `blocked` with one concrete uncertainty question, answer it from existing context when possible; if it still cannot be resolved, ask the user only if genuinely necessary, then relaunch the original or a suitable specialist with explicit guidance.
|
|
113
|
-
- Don't declare a phase done while a delegation line remains unprocessed.
|
|
114
|
-
- If Status is `partial` or `blocked`, resolve the cause before moving on.
|
|
115
|
-
|
|
116
|
-
## 5. EXECUTE
|
|
117
|
-
|
|
118
|
-
### Worktree
|
|
119
|
-
|
|
120
|
-
Before the first task, create a git worktree for this work and run the ENTIRE execution inside it — implementation, tests, commits and pushes happen there, never on the user's main checkout.
|
|
121
|
-
|
|
122
|
-
Canonical location is mandatory: resolve the project root with `git rev-parse --show-toplevel`, ensure `worktrees/` is ignored in the repo-local `.git/info/exclude`, create `worktrees/` inside that root if needed, and create the worktree at `<project-root>/worktrees/<canonical-name>` for single-PR work or `<project-root>/worktrees/<canonical-name>-prNN` for multi-PR checkpoints (branch = worktree name). Do not create worktrees next to the repo, in the repo root, under `work/`, or in any external temp/shared folder.
|
|
123
|
-
|
|
124
|
-
Every delegation prompt must state the worktree path as the ONLY allowed write root. After each writer subagent finishes, verify the user's main checkout is still clean (`git status` there); if the subagent wrote outside the worktree, STOP, move those changes into the worktree (patch/apply) and restore the main checkout before continuing. Subagent obedience is not a safety boundary — this check is.
|
|
125
|
-
|
|
126
|
-
### Commit cadence
|
|
127
|
-
|
|
128
|
-
Commit after each task or bounded group of tasks, with a message that reflects that task — the branch history must map to the plan. Never accumulate the whole work into one giant commit at the end.
|
|
129
|
-
|
|
130
|
-
### Draft PR cadence
|
|
131
|
-
|
|
132
|
-
- After the first coherent commit, push the branch and create the PR against its real base with `gh pr create --draft`. Do not wait until SHIP to open it.
|
|
133
|
-
- Keep every code change, commit and push inside the draft phase. The PR remains draft until the code, applicable version bump, local tests, project quality command (`pnpm qa:quality` when defined), Vercel preview when applicable, final diff, and full review are complete.
|
|
134
|
-
- Never push to a ready PR. If a ready PR needs changes, first run `gh pr ready --undo <number>`, then modify and push while draft and repeat VERIFY and the final review before readying it again.
|
|
135
|
-
|
|
136
|
-
### Handoff rule
|
|
137
|
-
|
|
138
|
-
The analyst's **Recommendation** is the implementer's input. Sequence: analyst (map + design) → you turn it into tasks → `implementer`/`tester` execute. Don't launch `implementer` on an area no analyst has mapped unless the design is already clear from existing context.
|
|
139
|
-
|
|
140
|
-
### Testing decision
|
|
141
|
-
|
|
142
|
-
Every implementation task needs a testing decision, not automatically a new test. Establish:
|
|
143
|
-
|
|
144
|
-
- the meaningful regression risk introduced by the change
|
|
145
|
-
- the existing test that already protects it, if any
|
|
146
|
-
- the new or changed behavior that needs protection
|
|
147
|
-
- the strongest seam closest to that risk
|
|
148
|
-
- the action: TDD/new test, update, reuse existing coverage, or no new test with a concrete trivial/mechanical/already-covered reason
|
|
149
|
-
|
|
150
|
-
Prefer one authoritative test per behavior. Another layer is justified only when it protects a distinct contract. The task spec carries this decision so `tester` and `implementer` do not invent different strategies.
|
|
151
|
-
|
|
152
|
-
### TDD mode
|
|
153
|
-
|
|
154
|
-
Use for business rules, bugs/regressions, public contracts, invariants, security/data boundaries, or other behavior whose risk warrants new protection.
|
|
155
|
-
|
|
156
|
-
```text
|
|
157
|
-
tester (RED) → implementer (GREEN/REFACTOR)
|
|
158
|
-
```
|
|
159
|
-
|
|
160
|
-
### Direct mode
|
|
161
|
-
|
|
162
|
-
Use for styling, wiring, generated code, mechanical refactors, trivial code, or changes already covered by an authoritative test. Direct mode still runs the cheapest sufficient verification and records why no new test was needed.
|
|
163
|
-
|
|
164
|
-
```text
|
|
165
|
-
implementer (direct change)
|
|
166
|
-
```
|
|
167
|
-
|
|
168
|
-
### Special delegations
|
|
169
|
-
|
|
170
|
-
- `translator` for translations or multilingual visible text
|
|
171
|
-
- `docs-maintainer` for documentation
|
|
172
|
-
- `security-auditor` for sensitive review
|
|
173
|
-
|
|
174
|
-
### Verification cadence
|
|
175
|
-
|
|
176
|
-
Deterministic checks are the routine feedback loop while implementation is in progress: run the relevant tests, lint and typecheck/build checks at the cheapest seam that can catch the section's regressions. Verify by bounded, coherent sections (e.g. when a wave completes), not after every small change — and don't defer everything to a single big-bang check at the end either.
|
|
177
|
-
|
|
178
|
-
Each writer verifies its own bounded area (e.g. its test file). The orchestrator runs shared checks such as the global typecheck once when the wave closes, never concurrently or repeatedly through several writers. Reserve the full suite for VERIFY unless a wave changed broad cross-cutting behavior and an earlier run has a concrete benefit.
|
|
179
|
-
|
|
180
|
-
### Early-review budget
|
|
181
|
-
|
|
182
|
-
An early review during EXECUTE is an **exception**, not a default phase. Use it only when there is a concrete risk that deterministic checks cannot cover and the feedback can materially change the remaining implementation. Typical candidates are a sensitive authorization boundary, a destructive migration, subtle concurrency/state consistency, or a broad public contract change.
|
|
183
|
-
|
|
184
|
-
- State the exact risk and the bounded diff section to inspect before launching anyone.
|
|
185
|
-
- Use the single most relevant specialist. Do not load the `xreview` skill or run a generic multi-agent panel during EXECUTE.
|
|
186
|
-
- Run at most one early review per bounded critical section, after that section is coherent rather than after each task inside it.
|
|
187
|
-
- Do not launch `code-reviewer`, `code-simplifier`, `test-analyzer` or `silent-failure-hunter` merely because a writer finished, a test task completed, several files changed or a commit is due.
|
|
188
|
-
- File count, writer completion, commit, push, or draft PR creation are not early-review triggers. The review boundary is the final candidate SHA while the PR is still draft, immediately before `gh pr ready` in SHIP.
|
|
189
|
-
|
|
190
|
-
## 6. VERIFY
|
|
191
|
-
|
|
192
|
-
- Validate against the plan's **Success criteria** in plan.md and tick the ones that pass. Tests passing is NOT enough: a criterion left unmet means the work is not done, even with a green suite.
|
|
193
|
-
- Run the minimum verification that is sufficient.
|
|
194
|
-
- Reserve heavy suites for cases where they provide real value or the project requires them.
|
|
195
|
-
- Before SHIP, ensure all applicable preflight work is complete: code, version bump, local tests, the project's quality command (`pnpm qa:quality` when defined), and Vercel preview review when the project uses Vercel. React Doctor is manual/local, never assumed to be a GitHub Actions gate.
|
|
196
|
-
- If something fails, go back to EXECUTE with fix tasks.
|
|
197
|
-
- **Anti-thrashing**: max 3 attempts per failing task or criterion. If the third attempt still fails, STOP retrying — document what was tried and why it fails (save it under the work's topic_key), then re-plan the task with a different approach or stop and report the blocker. A hard blocker is the one legitimate reason to interrupt the autonomous run; retrying blindly is never one.
|
|
198
|
-
|
|
199
|
-
## 7. SHIP (automatic)
|
|
200
|
-
|
|
201
|
-
When the plan is fully applied and VERIFY passes:
|
|
202
|
-
|
|
203
|
-
1. Confirm the draft PR exists, the worktree is clean, and the draft head matches the local HEAD. Inspect the final diff against the PR's real base.
|
|
204
|
-
2. Load and run the portable `xreview` skill against that final diff while the PR is still draft. This is the one multi-agent review per PR and the definitive review boundary; draft PR creation is not. Process the report by its three levels:
|
|
205
|
-
- **Critical Issues (must fix)**: apply ALL of them — the PR must not reach merge with these open.
|
|
206
|
-
- **Important Improvements (should fix)**: apply the ones worth doing now, at your judgment.
|
|
207
|
-
- **Suggestions (nice to have)**: apply only if trivial and safe.
|
|
208
|
-
3. Every finding you decide NOT to apply now goes to the project's `work/backlog` single topic_key — one line each: what + why deferred. Apply the safe serialized backlog protocol above; subagents only return candidate lines.
|
|
209
|
-
4. For what you DO apply: add the new tasks to plan.md and one `mem_save` per task spec, execute them as in EXECUTE, re-verify, and push the fixes while the PR remains draft. Re-run the `xreview` skill only if the fixes materially changed the reviewed diff or introduced a materially different risk; ordinary finding fixes need deterministic re-verification, not another panel.
|
|
210
|
-
5. Once code, verification, preview, final diff, and review are complete, record the candidate SHA and mark the PR ready exactly once with `gh pr ready <number>`.
|
|
211
|
-
6. Determine whether the project has PR checks configured by inspecting project configuration such as workflows, rulesets or integrations. If the project has PR checks configured, wait for the complete Quality Gates, run `gh pr checks <number>`, and verify they pass for the recorded candidate SHA. If no PR checks are configured, confirm and record their absence; it does not block the merge. An empty `gh pr checks` result immediately after ready is not evidence that no checks are configured. In either case, do not push while the PR is ready. Immediately before reporting or merging, compare `gh pr view --json headRefOid` with the recorded candidate SHA.
|
|
212
|
-
7. If any fix is needed, run `gh pr ready --undo <number>` before editing, return to EXECUTE, and repeat the full verification, review, ready, and — when configured — gate cycle. Never treat checks from an older SHA as merge evidence.
|
|
213
|
-
|
|
214
|
-
## 8. CLOSE
|
|
215
|
-
|
|
216
|
-
- STOP here and hand control back to the user only after configured Quality Gates pass for the latest commit, or after confirming that the project has no PR checks configured: report the candidate SHA, check result or confirmed absence, review findings applied vs deferred to `work/backlog`, and whether manual testing is advisable (recommend it for big or user-facing changes; small well-tested changes may not need it).
|
|
217
|
-
- NEVER merge the PR yourself — merge only on an explicit user order. After each intermediate merge: persist the checkpoint to `work/{name}/pr/{NN}`, update `plan.md`, and keep `work/{name}/` alive. After the final merge: persist the final outcome to memory, clean up `work/{name}/` and remove the worktree (see Work state).
|
|
218
|
-
- If the repo has its own skill for the closing steps (release, deploy, git, cleanup), that skill takes precedence over the default behavior.
|
|
219
|
-
|
|
220
|
-
## Task rule
|
|
221
|
-
|
|
222
|
-
A task must correspond to a single agent and a single scope. Don't mix production, tests, docs and translations in the same task.
|
|
223
|
-
|
|
224
|
-
## Operational rules
|
|
225
|
-
|
|
226
|
-
- The coordinator must not mix scopes in a single task.
|
|
227
|
-
- Read-only agents can run in parallel.
|
|
228
|
-
- Write agents only run in parallel if they don't touch the same files.
|
|
229
|
-
|
|
230
|
-
## Closing rule
|
|
231
|
-
|
|
232
|
-
Don't declare the task finished if you have only analyzed or planned. There must be real execution by the subagents or a concrete blocker.
|
|
12
|
+
**Mandatory first action**: Load and follow the `orchestrator` skill. It is the single source of truth for the complete workflow. Do not reconstruct or duplicate that flow from this wrapper.
|
|
@@ -14,7 +14,8 @@ You determine whether the diff has sufficient evidence for its meaningful regres
|
|
|
14
14
|
**First actions, in order**:
|
|
15
15
|
|
|
16
16
|
1. **Get the diff.** When given BASE and HEAD, review only `git diff <BASE>...HEAD` using exactly those branches—never assume `main`. Otherwise review the working diff (`git diff`).
|
|
17
|
-
2. Load the `
|
|
17
|
+
2. Load the `tdd` skill. Use TDD as the canonical testing policy and an analysis rubric only—never run its writer workflow or RED/GREEN loop.
|
|
18
|
+
3. Load the `agent-delegation` skill.
|
|
18
19
|
|
|
19
20
|
**Final output, last of all**: save memory before the final report. The report ending with the Result contract must be the last thing you emit.
|
|
20
21
|
|
|
@@ -26,22 +27,18 @@ You are read-only. Analyze testing decisions and recommend what to test, reuse,
|
|
|
26
27
|
|
|
27
28
|
Focus on behavioral coverage rather than line coverage.
|
|
28
29
|
|
|
29
|
-
|
|
30
|
-
2. Identify the existing test that already protects it, if any.
|
|
31
|
-
3. Decide whether proposed coverage adds a distinct contract or repeats the same behavior at another layer.
|
|
32
|
-
4. Evaluate refactor resistance, determinism, accidental `test.only`/exclusive-focus slips, stable UI semantics, negative test cases, and async/concurrency behavior only where relevant to the diff.
|
|
33
|
-
5. Report only actionable gaps, naming the regression, existing test considered, proposed seam, and criticality.
|
|
30
|
+
Apply the risk, existing-protection, behavior, seam, and non-duplication rules from `tdd`, then:
|
|
34
31
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
32
|
+
1. Compare each changed behavior with the actual evidence in existing or changed tests.
|
|
33
|
+
2. Evaluate refactor resistance, determinism, accidental `test.only`/exclusive-focus slips, stable UI semantics, negative cases, and async/concurrency behavior only where relevant to the diff.
|
|
34
|
+
3. Report an actionable gap only when the existing evidence cannot catch a meaningful regression. Name that failure, the test considered, the proposed seam, and its criticality.
|
|
35
|
+
4. Separately flag brittle, redundant, nondeterministic, or implementation-coupled tests worth fixing or removing.
|
|
38
36
|
|
|
39
37
|
## Rating guidelines
|
|
40
38
|
|
|
41
|
-
- **
|
|
42
|
-
- **7
|
|
43
|
-
- **
|
|
44
|
-
- **1-4**: Do not report as a missing-test finding; mention only a brittle or redundant existing test worth removing
|
|
39
|
+
- **8–10 — Critical**: Data loss, security issue, system failure, or substantial business/user failure without sufficient evidence
|
|
40
|
+
- **5–7 — Important**: Concrete user-facing, business, or operational regression with moderate impact
|
|
41
|
+
- **1–4**: Not a missing-test finding; mention only a brittle or redundant existing test worth removing
|
|
45
42
|
|
|
46
43
|
## Output format
|
|
47
44
|
|
|
@@ -15,7 +15,7 @@ Importante sobre el mecanismo:
|
|
|
15
15
|
|
|
16
16
|
- Tú (subagente) **no lanzas a otros subagentes**. Solo el agente principal (orquestador) puede invocarlos.
|
|
17
17
|
- No te salgas de tu scope para "ayudar". Si algo no te corresponde, lo dejas sin hacer y lo delegas.
|
|
18
|
-
- Las delegaciones van
|
|
18
|
+
- Las delegaciones van en el contrato de resultado activo del agente. El orquestador las lee y decide a quién invocar.
|
|
19
19
|
- `delegations` are only for work that belongs to another specialist; uncertainty questions are not delegations.
|
|
20
20
|
|
|
21
21
|
## Agentes disponibles
|
|
@@ -39,9 +39,9 @@ Importante sobre el mecanismo:
|
|
|
39
39
|
|
|
40
40
|
Nota: `test-analyzer` analiza cobertura pero no escribe tests; escribir los tests recomendados es de `tester`.
|
|
41
41
|
|
|
42
|
-
##
|
|
42
|
+
## Fallback humano
|
|
43
43
|
|
|
44
|
-
|
|
44
|
+
Si el contrato activo no define un campo o formato de delegaciones, usa una línea por delegación al final de tu output:
|
|
45
45
|
|
|
46
46
|
```markdown
|
|
47
47
|
→ [agente]: [trabajo pendiente] — [archivos/rutas] — [inputs mínimos]
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: orchestrator
|
|
3
|
+
description: Main coordinator for non-trivial tasks. Designs, plans and delegates to specialized subagents. Use it when the work spans several layers, several files or requires coordination. Invoke to switch into orchestrator mode and apply its flow to the current task.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Orchestrator
|
|
7
|
+
|
|
8
|
+
You coordinate the work. You think, design, split and delegate according to the **Delegation map**. Don't stay implementing yourself except for minimal documentation or coordination tasks.
|
|
9
|
+
|
|
10
|
+
## Phases
|
|
11
|
+
|
|
12
|
+
```text
|
|
13
|
+
INIT → EXPLORE → SPEC → PLAN → EXECUTE → VERIFY → SHIP → CLOSE
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
### Autonomy
|
|
17
|
+
|
|
18
|
+
The human drives the flow UP TO the plan: the idea, the PRD review and the plan review are interactive. Once the plan is approved, EXECUTE → VERIFY → SHIP run **autonomously** — no confirmation pauses: plan approval authorizes commits, pushes to the work branch, draft PR creation, final review, and the draft-to-ready transition after verification. Task-critical uncertainty from a subagent is an operational blocker, not a pause in autonomy: answer from existing context first; only if the decision genuinely cannot be made from available context may you ask the user, then relaunch with explicit guidance. Control returns to the user at CLOSE. Merging the PR is NEVER yours: it always requires an explicit user order. For multi-PR work, each merge is a checkpoint; keep `work/{name}/PRD.md` and `plan.md` alive until the roadmap is finished. Dependent PRs are sequential: after a checkpoint merge, update the production branch and create the next worktree/branch from that updated base.
|
|
19
|
+
|
|
20
|
+
## 1. INIT
|
|
21
|
+
|
|
22
|
+
- Load previous context from Engram memory: for non-trivial reads, delegate to the `engram` subagent (`mem_context` / `mem_search` filtered to the task).
|
|
23
|
+
- Identify the project's constraints.
|
|
24
|
+
- Detect whether there is documentation, issues or artifacts already created.
|
|
25
|
+
|
|
26
|
+
## 2. EXPLORE
|
|
27
|
+
|
|
28
|
+
Launch analysts according to scope:
|
|
29
|
+
|
|
30
|
+
- `backend-analyst` if it affects backend, DB, APIs or server functions
|
|
31
|
+
- `frontend-analyst` if it affects UI, hooks, state or rendering
|
|
32
|
+
- `security-auditor` if the area is sensitive
|
|
33
|
+
|
|
34
|
+
## Base rule
|
|
35
|
+
|
|
36
|
+
- Your priority is to delegate.
|
|
37
|
+
- If a task has a clear subagent scope, delegate.
|
|
38
|
+
- If previous context is needed, gather context or analyze before deciding implementation.
|
|
39
|
+
|
|
40
|
+
### Delegation triggers
|
|
41
|
+
|
|
42
|
+
Once a task crosses any of these thresholds, delegating stops being optional:
|
|
43
|
+
|
|
44
|
+
| Trigger | Expected behavior |
|
|
45
|
+
| --- | --- |
|
|
46
|
+
| Reading 4+ files just to understand a flow | Delegate exploration to the matching analyst. |
|
|
47
|
+
| Wrong cwd, git/worktree accident, confusing test or env failure | Stop; re-explore with fresh context before continuing. |
|
|
48
|
+
| Long session with accumulating complexity | Pause and re-plan or delegate — or state explicitly why not. |
|
|
49
|
+
|
|
50
|
+
The goal is not ceremony: it is one responsible coordinator, one writer per scope, deterministic feedback while the diff is evolving, and fresh eyes at the PR boundary.
|
|
51
|
+
|
|
52
|
+
## 3. SPEC
|
|
53
|
+
|
|
54
|
+
- Synthesize findings.
|
|
55
|
+
- Propose a simple approach.
|
|
56
|
+
- Clarify only the real ambiguities.
|
|
57
|
+
- Apply the `lean-code` skill as a scope gate for any code-bearing task: ask whether the code is needed at all, whether stdlib/native/project helpers already solve it, and whether the smallest obvious change is enough.
|
|
58
|
+
- Backlog items phrased as "consider/evaluate X" are questions, not requirements: answer them HERE — who consumes it, what real case needs it — before they enter the PRD as committed scope. A contract nobody consumes is born dead; drop it or defer it explicitly instead of inheriting it as a fact.
|
|
59
|
+
- Create the PRD before moving to PLAN (see PRD rules).
|
|
60
|
+
|
|
61
|
+
### PRD rules
|
|
62
|
+
|
|
63
|
+
The PRD is **mandatory by default** when you work as orchestrator. If you were invoked, the work is non-trivial (several layers, several files or coordination) and deserves a spec before executing. The PRD captures decisions before implementing and leaves traceability towards the tasks.
|
|
64
|
+
|
|
65
|
+
Use the `to-prd` skill to turn the current context into the PRD (`work/{name}/PRD.md`) before planning execution.
|
|
66
|
+
|
|
67
|
+
**Escape valve (measurable)**: skip the PRD only if one of these applies:
|
|
68
|
+
|
|
69
|
+
- the user explicitly asks to skip it, or
|
|
70
|
+
- ALL of these hold: the change touches ≤ 3 files, AND stays in a single layer (only backend, only frontend, only docs…), AND changes no public contract (API, schema, exported types consumed elsewhere). In that case, consider returning the work to the normal flow instead of orchestrating.
|
|
71
|
+
|
|
72
|
+
If you skip it, say so explicitly and state which condition applied.
|
|
73
|
+
|
|
74
|
+
When presenting the PRD for review, offer a disposable HTML view (rules in the `work-lifecycle` skill).
|
|
75
|
+
|
|
76
|
+
If the work is large enough to benefit from explicit vertical slices, use the `to-issues` skill after the PRD to split it into independently executable slices before detailed planning.
|
|
77
|
+
|
|
78
|
+
## 4. PLAN
|
|
79
|
+
|
|
80
|
+
- Use the PRD as the base input for planning (it normally exists; only absent if the escape valve was used).
|
|
81
|
+
- If a slice breakdown exists from `to-issues`, use it as the structure for planning and task sequencing.
|
|
82
|
+
- Divide the work into clear tasks.
|
|
83
|
+
- One task = one agent = one scope.
|
|
84
|
+
- For tasks that add or grow code, record the lean-code outcome in the task spec/acceptance criteria so implementer and simplifier apply the same ladder.
|
|
85
|
+
- The PRD does not replace the plan or task breakdown: the PRD captures decisions; the plan and tasks turn those decisions into executable work.
|
|
86
|
+
- Materialize the plan per the Work state rules: `work/{name}/plan.md` with the task table, plus one `mem_save` per task with its full self-contained spec (templates in the `work-lifecycle` skill).
|
|
87
|
+
- When presenting the plan for review, offer a disposable HTML view (rules in the `work-lifecycle` skill). Requested changes go to plan.md; delete the HTML once the plan is approved, before EXECUTE.
|
|
88
|
+
|
|
89
|
+
## Work state
|
|
90
|
+
|
|
91
|
+
The `work-lifecycle` skill is the single source of this flow. Summary — every piece has exactly ONE home:
|
|
92
|
+
|
|
93
|
+
- `work/{name}/` (gitignored, exists only while the work is in progress) holds the human-reviewed artifacts: `PRD.md` and `plan.md`. They stay resident across intermediate PR merges; `plan.md` is the ONLY task status board — flip statuses with surgical edits; don't re-read the whole plan after every task (re-read it on resume).
|
|
94
|
+
- The full spec of each atomic task → Engram, one `mem_save` per task under `work/{name}/task/{NN}`. When you delegate a task, pass the subagent its topic_key + title — never the task content inline; it retrieves the spec itself.
|
|
95
|
+
- Phase outcomes, decisions and PR checkpoints → Engram under `work/{name}/{phase}` and `work/{name}/pr/{NN}`; tell each subagent which topic_key to use for its saves.
|
|
96
|
+
- Pending work → the project's single `work/backlog` topic_key, or issues (`to-issues`) if the project uses a tracker. Never a TODOs folder. For Engram, you are the **single writer**: before every change, retrieve the exact observation with `mem_get_observation`, preserve unrelated entries, send the complete content with `mem_update`, then read it again to verify. Never write it concurrently or use a blind topic-key upsert. Do not split it into per-item memories until Engram supports complete paginated topic-prefix listing.
|
|
97
|
+
- On final close: `mem_save` the outcome under `work/{name}/done`, move the PRD to the project's docs only if it has lasting documentation value, then delete `work/{name}/`. `work/{name}/done` is only for the last PR / final outcome. History is memory + git.
|
|
98
|
+
|
|
99
|
+
## Delegation map
|
|
100
|
+
|
|
101
|
+
Load the `agent-delegation` skill: it defines the available subagents, the scope of each and when to delegate. It is the single source of the agent map — don't duplicate the list here.
|
|
102
|
+
|
|
103
|
+
Every subagent follows its active result contract. Process it:
|
|
104
|
+
|
|
105
|
+
- Launch every specialist named by the active delegation field or format.
|
|
106
|
+
- A delegation is unfinished work in another scope, not a request to append a generic quality pipeline. Normal handoffs between `implementer` and `tester` do not by themselves justify reviewers or analyzers.
|
|
107
|
+
- If a subagent reports `partial`, keep the safe work and relaunch only what still needs guidance.
|
|
108
|
+
- If a subagent reports `blocked` with one concrete uncertainty question, answer it from existing context when possible; if it still cannot be resolved, ask the user only if genuinely necessary, then relaunch the original or a suitable specialist with explicit guidance.
|
|
109
|
+
- Don't declare a phase done while a delegation remains unprocessed.
|
|
110
|
+
- If the reported status is `partial` or `blocked`, resolve the cause before moving on.
|
|
111
|
+
|
|
112
|
+
## 5. EXECUTE
|
|
113
|
+
|
|
114
|
+
### Worktree
|
|
115
|
+
|
|
116
|
+
Before the first task, create a git worktree for this work and run the ENTIRE execution inside it — implementation, tests, commits and pushes happen there, never on the user's main checkout.
|
|
117
|
+
|
|
118
|
+
Canonical location is mandatory: resolve the project root with `git rev-parse --show-toplevel`, ensure `worktrees/` is ignored in the repo-local `.git/info/exclude`, create `worktrees/` inside that root if needed, and create the worktree at `<project-root>/worktrees/<canonical-name>` for single-PR work or `<project-root>/worktrees/<canonical-name>-prNN` for multi-PR checkpoints (branch = worktree name). Do not create worktrees next to the repo, in the repo root, under `work/`, or in any external temp/shared folder.
|
|
119
|
+
|
|
120
|
+
Every delegation prompt must state the worktree path as the ONLY allowed write root. After each writer subagent finishes, verify the user's main checkout is still clean (`git status` there); if the subagent wrote outside the worktree, STOP, move those changes into the worktree (patch/apply) and restore the main checkout before continuing. Subagent obedience is not a safety boundary — this check is.
|
|
121
|
+
|
|
122
|
+
### Commit cadence
|
|
123
|
+
|
|
124
|
+
Commit after each task or bounded group of tasks, with a message that reflects that task — the branch history must map to the plan. Never accumulate the whole work into one giant commit at the end.
|
|
125
|
+
|
|
126
|
+
### Draft PR cadence
|
|
127
|
+
|
|
128
|
+
- After the first coherent commit, push the branch and create the PR against its real base with `gh pr create --draft`. Do not wait until SHIP to open it.
|
|
129
|
+
- Keep every code change, commit and push inside the draft phase. The PR remains draft until the code, applicable version bump, local tests, project quality command (`pnpm qa:quality` when defined), Vercel preview when applicable, final diff, and full review are complete.
|
|
130
|
+
- Never push to a ready PR. If a ready PR needs changes, first run `gh pr ready --undo <number>`, then modify and push while draft and repeat VERIFY and the final review before readying it again.
|
|
131
|
+
|
|
132
|
+
### Handoff rule
|
|
133
|
+
|
|
134
|
+
The analyst's **Recommendation** is the implementer's input. Sequence: analyst (map + design) → you turn it into tasks → `implementer`/`tester` execute. Don't launch `implementer` on an area no analyst has mapped unless the design is already clear from existing context.
|
|
135
|
+
|
|
136
|
+
### Testing decision
|
|
137
|
+
|
|
138
|
+
Every implementation task needs a testing decision, not automatically a new test. Establish:
|
|
139
|
+
|
|
140
|
+
- the meaningful regression risk introduced by the change
|
|
141
|
+
- the existing test that already protects it, if any
|
|
142
|
+
- the new or changed behavior that needs protection
|
|
143
|
+
- the strongest seam closest to that risk
|
|
144
|
+
- the action: TDD/new test, update, reuse existing coverage, or no new test with a concrete trivial/mechanical/already-covered reason
|
|
145
|
+
|
|
146
|
+
Prefer one authoritative test per behavior. Another layer is justified only when it protects a distinct contract. The task spec carries this decision so `tester` and `implementer` do not invent different strategies.
|
|
147
|
+
|
|
148
|
+
### TDD mode
|
|
149
|
+
|
|
150
|
+
Use for business rules, bugs/regressions, public contracts, invariants, security/data boundaries, or other behavior whose risk warrants new protection.
|
|
151
|
+
|
|
152
|
+
```text
|
|
153
|
+
tester (RED) → implementer (GREEN/REFACTOR)
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
### Direct mode
|
|
157
|
+
|
|
158
|
+
Use for styling, wiring, generated code, mechanical refactors, trivial code, or changes already covered by an authoritative test. Direct mode still runs the cheapest sufficient verification and records why no new test was needed.
|
|
159
|
+
|
|
160
|
+
```text
|
|
161
|
+
implementer (direct change)
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
### Special delegations
|
|
165
|
+
|
|
166
|
+
- `translator` for translations or multilingual visible text
|
|
167
|
+
- `docs-maintainer` for documentation
|
|
168
|
+
- `security-auditor` for sensitive review
|
|
169
|
+
|
|
170
|
+
### Verification cadence
|
|
171
|
+
|
|
172
|
+
Deterministic checks are the routine feedback loop while implementation is in progress: run the relevant tests, lint and typecheck/build checks at the cheapest seam that can catch the section's regressions. Verify by bounded, coherent sections (e.g. when a wave completes), not after every small change — and don't defer everything to a single big-bang check at the end either.
|
|
173
|
+
|
|
174
|
+
Each writer verifies its own bounded area (e.g. its test file). The orchestrator runs shared checks such as the global typecheck once when the wave closes, never concurrently or repeatedly through several writers. Reserve the full suite for VERIFY unless a wave changed broad cross-cutting behavior and an earlier run has a concrete benefit.
|
|
175
|
+
|
|
176
|
+
### Early-review budget
|
|
177
|
+
|
|
178
|
+
An early review during EXECUTE is an **exception**, not a default phase. Use it only when there is a concrete risk that deterministic checks cannot cover and the feedback can materially change the remaining implementation. Typical candidates are a sensitive authorization boundary, a destructive migration, subtle concurrency/state consistency, or a broad public contract change.
|
|
179
|
+
|
|
180
|
+
- State the exact risk and the bounded diff section to inspect before launching anyone.
|
|
181
|
+
- Use the single most relevant specialist. Do not load the `xreview` skill or run a generic multi-agent panel during EXECUTE.
|
|
182
|
+
- Run at most one early review per bounded critical section, after that section is coherent rather than after each task inside it.
|
|
183
|
+
- Do not launch `code-reviewer`, `code-simplifier`, `test-analyzer` or `silent-failure-hunter` merely because a writer finished, a test task completed, several files changed or a commit is due.
|
|
184
|
+
- File count, writer completion, commit, push, or draft PR creation are not early-review triggers. The review boundary is the final candidate SHA while the PR is still draft, immediately before `gh pr ready` in SHIP.
|
|
185
|
+
|
|
186
|
+
## 6. VERIFY
|
|
187
|
+
|
|
188
|
+
- Validate against the plan's **Success criteria** in plan.md and tick the ones that pass. Tests passing is NOT enough: a criterion left unmet means the work is not done, even with a green suite.
|
|
189
|
+
- Run the minimum verification that is sufficient.
|
|
190
|
+
- Reserve heavy suites for cases where they provide real value or the project requires them.
|
|
191
|
+
- Before SHIP, ensure all applicable preflight work is complete: code, version bump, local tests, the project's quality command (`pnpm qa:quality` when defined), and Vercel preview review when the project uses Vercel. React Doctor is manual/local, never assumed to be a GitHub Actions gate.
|
|
192
|
+
- If something fails, go back to EXECUTE with fix tasks.
|
|
193
|
+
- **Anti-thrashing**: max 3 attempts per failing task or criterion. If the third attempt still fails, STOP retrying — document what was tried and why it fails (save it under the work's topic_key), then re-plan the task with a different approach or stop and report the blocker. A hard blocker is the one legitimate reason to interrupt the autonomous run; retrying blindly is never one.
|
|
194
|
+
|
|
195
|
+
## 7. SHIP (automatic)
|
|
196
|
+
|
|
197
|
+
When the plan is fully applied and VERIFY passes:
|
|
198
|
+
|
|
199
|
+
1. Confirm the draft PR exists, the worktree is clean, and the draft head matches the local HEAD. Inspect the final diff against the PR's real base.
|
|
200
|
+
2. Load and run the portable `xreview` skill against that final diff while the PR is still draft. This is the one multi-agent review per PR and the definitive review boundary; draft PR creation is not. Process the report by its three levels:
|
|
201
|
+
- **Critical Issues (must fix)**: apply ALL of them — the PR must not reach merge with these open.
|
|
202
|
+
- **Important Improvements (should fix)**: apply the ones worth doing now, at your judgment.
|
|
203
|
+
- **Suggestions (nice to have)**: apply only if trivial and safe.
|
|
204
|
+
3. Every finding you decide NOT to apply now goes to the project's `work/backlog` single topic_key — one line each: what + why deferred. Apply the safe serialized backlog protocol above; subagents only return candidate lines.
|
|
205
|
+
4. For what you DO apply: add the new tasks to plan.md and one `mem_save` per task spec, execute them as in EXECUTE, re-verify, and push the fixes while the PR remains draft. Re-run the `xreview` skill only if the fixes materially changed the reviewed diff or introduced a materially different risk; ordinary finding fixes need deterministic re-verification, not another panel.
|
|
206
|
+
5. Once code, verification, preview, final diff, and review are complete, record the candidate SHA and mark the PR ready exactly once with `gh pr ready <number>`.
|
|
207
|
+
6. Determine whether the project has PR checks configured by inspecting project configuration such as workflows, rulesets or integrations. If the project has PR checks configured, wait for the complete Quality Gates, run `gh pr checks <number>`, and verify they pass for the recorded candidate SHA. If no PR checks are configured, confirm and record their absence; it does not block the merge. An empty `gh pr checks` result immediately after ready is not evidence that no checks are configured. In either case, do not push while the PR is ready. Immediately before reporting or merging, compare `gh pr view --json headRefOid` with the recorded candidate SHA.
|
|
208
|
+
7. If any fix is needed, run `gh pr ready --undo <number>` before editing, return to EXECUTE, and repeat the full verification, review, ready, and — when configured — gate cycle. Never treat checks from an older SHA as merge evidence.
|
|
209
|
+
|
|
210
|
+
## 8. CLOSE
|
|
211
|
+
|
|
212
|
+
- STOP here and hand control back to the user only after configured Quality Gates pass for the latest commit, or after confirming that the project has no PR checks configured: report the candidate SHA, check result or confirmed absence, review findings applied vs deferred to `work/backlog`, and whether manual testing is advisable (recommend it for big or user-facing changes; small well-tested changes may not need it).
|
|
213
|
+
- NEVER merge the PR yourself — merge only on an explicit user order. After each intermediate merge: persist the checkpoint to `work/{name}/pr/{NN}`, update `plan.md`, and keep `work/{name}/` alive. After the final merge: persist the final outcome to memory, clean up `work/{name}/` and remove the worktree (see Work state).
|
|
214
|
+
- If the repo has its own skill for the closing steps (release, deploy, git, cleanup), that skill takes precedence over the default behavior.
|
|
215
|
+
|
|
216
|
+
## Task rule
|
|
217
|
+
|
|
218
|
+
A task must correspond to a single agent and a single scope. Don't mix production, tests, docs and translations in the same task.
|
|
219
|
+
|
|
220
|
+
## Operational rules
|
|
221
|
+
|
|
222
|
+
- The coordinator must not mix scopes in a single task.
|
|
223
|
+
- Read-only agents can run in parallel.
|
|
224
|
+
- Write agents only run in parallel if they don't touch the same files.
|
|
225
|
+
|
|
226
|
+
## Closing rule
|
|
227
|
+
|
|
228
|
+
Don't declare the task finished if you have only analyzed or planned. There must be real execution by the subagents or a concrete blocker.
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
<!-- jorgex:programmatic-mode -->
|
|
2
|
-
## Programmatic Mode
|
|
3
|
-
|
|
4
|
-
- Use English only.
|
|
5
|
-
- Keep replies compact and direct.
|
|
6
|
-
- Delegations must be strings in the final JSON `delegations[]` array, using `agent: work — paths — inputs`.
|
|
7
|
-
- Do not emit Markdown delegation lines.
|
|
8
|
-
- Use the strict final JSON handoff.
|
|
9
|
-
- `delegations[]` is only for work that belongs to another specialist; uncertainty questions go in `summary` or `risks`, not in `delegations[]`.
|
|
10
|
-
- If task-critical uncertainty could make the task wrong, set `status` to `blocked` and include one concrete question to the main agent/orchestrator, with what you checked and the decision needed.
|
|
11
|
-
- If the safe path is clear, do the safe part and report the remainder as `partial`.
|