@agntn/harnesses 0.2.0 → 0.2.1
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 +36 -13
- package/dist/_chunks/agents.mjs +2 -30
- package/dist/_chunks/agents.mjs.map +1 -1
- package/dist/_chunks/libs/typebox.mjs +11 -8
- package/dist/_chunks/libs/typebox.mjs.map +1 -1
- package/dist/_chunks/mcp-servers.mjs +5 -34
- package/dist/_chunks/mcp-servers.mjs.map +1 -1
- package/dist/_chunks/mcp.mjs +13 -163
- package/dist/_chunks/mcp.mjs.map +1 -1
- package/dist/_chunks/output.mjs +28 -0
- package/dist/_chunks/output.mjs.map +1 -0
- package/dist/_chunks/{agents-sync.d.mts → prompt-sync.d.mts} +51 -3
- package/dist/_chunks/prompt-sync.d.mts.map +1 -0
- package/dist/_chunks/{agents-sync.mjs → prompt-sync.mjs} +517 -21
- package/dist/_chunks/prompt-sync.mjs.map +1 -0
- package/dist/_chunks/prompts.mjs +28 -0
- package/dist/_chunks/prompts.mjs.map +1 -0
- package/dist/_chunks/tool-operations.mjs +43 -10
- package/dist/_chunks/tool-operations.mjs.map +1 -1
- package/dist/_chunks/tool-schemas.d.mts +5 -0
- package/dist/_chunks/tool-schemas.d.mts.map +1 -1
- package/dist/_chunks/tool-schemas.mjs +4 -0
- package/dist/_chunks/tool-schemas.mjs.map +1 -1
- package/dist/_chunks/types.mjs +1 -1
- package/dist/_chunks/types.mjs.map +1 -1
- package/dist/cli.mjs +6 -2
- package/dist/cli.mjs.map +1 -1
- package/dist/index.d.mts +16 -2
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +2 -2
- package/dist/index.mjs.map +1 -1
- package/dist/mcp.d.mts.map +1 -1
- package/dist/mcp.mjs +176 -1
- package/dist/mcp.mjs.map +1 -0
- package/dist/tool-operations.d.mts +12 -2
- package/dist/tool-operations.d.mts.map +1 -1
- package/dist/tool-operations.mjs +2 -2
- package/dist/tool-schemas.d.mts +2 -2
- package/package.json +16 -14
- package/packages/omp/extensions/harnesses.ts +19 -1
- package/packages/pi/extensions/harnesses.ts +23 -1
- package/packages/shared/tui.ts +22 -1
- package/dist/_chunks/agents-sync.d.mts.map +0 -1
- package/dist/_chunks/agents-sync.mjs.map +0 -1
- package/dist/_chunks/mcp2.mjs +0 -17
- package/dist/_chunks/mcp2.mjs.map +0 -1
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import { createRequire } from "node:module";
|
|
2
2
|
import { addAbortListener } from "node:events";
|
|
3
|
-
import { existsSync, lstatSync, mkdirSync, readFileSync, readlinkSync, realpathSync, renameSync, symlinkSync, unlinkSync, writeFileSync } from "node:fs";
|
|
3
|
+
import { copyFileSync, existsSync, lstatSync, mkdirSync, readFileSync, readdirSync, readlinkSync, realpathSync, renameSync, symlinkSync, unlinkSync, writeFileSync } from "node:fs";
|
|
4
4
|
import { execFile, execFileSync, spawn } from "node:child_process";
|
|
5
5
|
import { setTimeout as setTimeout$1 } from "node:timers/promises";
|
|
6
|
-
import { basename, dirname, isAbsolute, join, posix, resolve, win32 } from "node:path";
|
|
6
|
+
import { basename, dirname, extname, isAbsolute, join, posix, relative, resolve, win32 } from "node:path";
|
|
7
7
|
import os, { homedir } from "node:os";
|
|
8
8
|
import { stripVTControlCharacters } from "node:util";
|
|
9
9
|
import { parse } from "smol-toml";
|
|
10
|
+
import { randomUUID } from "node:crypto";
|
|
11
|
+
import { parse as parse$1 } from "yaml";
|
|
10
12
|
const PATH_MARKERS = /^~(?=\/|$)|\$\{HOME\}|\$\{PROJECT_ROOT\}|%([^%]+)%/g;
|
|
11
13
|
function resolvePathTemplate(template, options = {}) {
|
|
12
14
|
const homeDir = options.homeDir ?? os.homedir();
|
|
@@ -21,6 +23,10 @@ function agntnConfigDir(options = {}) {
|
|
|
21
23
|
const base = process.env.XDG_CONFIG_HOME && process.env.XDG_CONFIG_HOME !== "" ? process.env.XDG_CONFIG_HOME : join(options.homeDir ?? os.homedir(), ".config");
|
|
22
24
|
return join(base, "agntn");
|
|
23
25
|
}
|
|
26
|
+
function agntnDataDir(options = {}) {
|
|
27
|
+
const base = process.env.XDG_DATA_HOME && process.env.XDG_DATA_HOME !== "" ? process.env.XDG_DATA_HOME : join(options.homeDir ?? os.homedir(), ".local", "share");
|
|
28
|
+
return join(base, "agntn");
|
|
29
|
+
}
|
|
24
30
|
const SUPPORTED_PLATFORMS = {
|
|
25
31
|
linux: true,
|
|
26
32
|
darwin: true,
|
|
@@ -197,6 +203,8 @@ function executeCommand(command, args, options) {
|
|
|
197
203
|
});
|
|
198
204
|
}
|
|
199
205
|
var Harness = class {
|
|
206
|
+
promptTemplates = [];
|
|
207
|
+
promptTemplateSyncTarget = null;
|
|
200
208
|
modelListing = null;
|
|
201
209
|
mcpConfigs = [];
|
|
202
210
|
agentsFile = null;
|
|
@@ -325,6 +333,8 @@ var Harness = class {
|
|
|
325
333
|
instructions: this.resolveCandidates(this.instructions, options),
|
|
326
334
|
skills: this.resolveCandidates(this.skills, options),
|
|
327
335
|
commands: this.resolveCandidates(this.commands, options),
|
|
336
|
+
promptTemplates: this.resolveCandidates(this.promptTemplates, options),
|
|
337
|
+
promptTemplateSyncTarget: this.promptTemplateSyncTarget === null ? null : this.resolveCandidates([this.promptTemplateSyncTarget], options)[0] ?? null,
|
|
328
338
|
hooks: this.resolveCandidates(this.hooks, options)
|
|
329
339
|
};
|
|
330
340
|
}
|
|
@@ -602,6 +612,13 @@ var Claude = class extends Harness {
|
|
|
602
612
|
level: "official",
|
|
603
613
|
note: "Legacy global slash commands."
|
|
604
614
|
}];
|
|
615
|
+
promptTemplates = this.commands;
|
|
616
|
+
promptTemplateSyncTarget = {
|
|
617
|
+
path: "~/.claude/commands/",
|
|
618
|
+
scope: "user",
|
|
619
|
+
level: "official",
|
|
620
|
+
format: "markdown"
|
|
621
|
+
};
|
|
605
622
|
hooks = [{
|
|
606
623
|
path: ".claude/hooks/",
|
|
607
624
|
scope: "project",
|
|
@@ -623,6 +640,7 @@ var Claude = class extends Harness {
|
|
|
623
640
|
noToolsArgs: [
|
|
624
641
|
"-p",
|
|
625
642
|
"{prompt}",
|
|
643
|
+
"--strict-mcp-config",
|
|
626
644
|
"--tools",
|
|
627
645
|
""
|
|
628
646
|
],
|
|
@@ -631,6 +649,7 @@ var Claude = class extends Harness {
|
|
|
631
649
|
"--output-format",
|
|
632
650
|
"json",
|
|
633
651
|
"{prompt}",
|
|
652
|
+
"--strict-mcp-config",
|
|
634
653
|
"--tools",
|
|
635
654
|
""
|
|
636
655
|
],
|
|
@@ -653,7 +672,7 @@ var Claude = class extends Harness {
|
|
|
653
672
|
readOnlyMinVersion: "2.1.175",
|
|
654
673
|
modelArgs: ["--model", "{model}"],
|
|
655
674
|
level: "official",
|
|
656
|
-
note: "Headless print mode; add --output-format json for structured output.
|
|
675
|
+
note: "Headless print mode; add --output-format json for structured output. --tools only covers the built-in set, so every mode without the full agent adds --strict-mcp-config to drop configured MCP servers too; without it an advisor still sees every user and project MCP tool, checked on 2.1.280. Read-only runs keep the built-in Read, Glob and Grep tools, so no write tool exists whatever permission mode the settings carry; verified on 2.1.175 and 2.1.268. --permission-mode plan does not qualify: its shell commands run through the auto mode classifier, which let a file write through."
|
|
657
676
|
};
|
|
658
677
|
mcpConfigs = [{
|
|
659
678
|
path: "~/.claude.json",
|
|
@@ -808,7 +827,19 @@ var Codex = class extends Harness {
|
|
|
808
827
|
platforms: ["linux", "darwin"]
|
|
809
828
|
}
|
|
810
829
|
];
|
|
811
|
-
commands = [
|
|
830
|
+
commands = [{
|
|
831
|
+
path: "~/.codex/prompts/",
|
|
832
|
+
scope: "user",
|
|
833
|
+
level: "official",
|
|
834
|
+
note: "Deprecated Markdown prompts invoked as /prompts:name. Top-level files only; use skills for shared prompts. Base directory follows CODEX_HOME."
|
|
835
|
+
}];
|
|
836
|
+
promptTemplates = this.commands;
|
|
837
|
+
promptTemplateSyncTarget = {
|
|
838
|
+
path: "~/.codex/prompts/",
|
|
839
|
+
scope: "user",
|
|
840
|
+
level: "official",
|
|
841
|
+
format: "markdown"
|
|
842
|
+
};
|
|
812
843
|
hooks = [];
|
|
813
844
|
invocation = {
|
|
814
845
|
args: [
|
|
@@ -1171,6 +1202,13 @@ var Gemini = class extends Harness {
|
|
|
1171
1202
|
level: "official",
|
|
1172
1203
|
note: "Global custom commands in TOML format."
|
|
1173
1204
|
}];
|
|
1205
|
+
promptTemplates = this.commands;
|
|
1206
|
+
promptTemplateSyncTarget = {
|
|
1207
|
+
path: "~/.gemini/commands/",
|
|
1208
|
+
scope: "user",
|
|
1209
|
+
level: "official",
|
|
1210
|
+
format: "gemini-toml"
|
|
1211
|
+
};
|
|
1174
1212
|
hooks = [];
|
|
1175
1213
|
invocation = {
|
|
1176
1214
|
args: ["-p", "{prompt}"],
|
|
@@ -1488,6 +1526,12 @@ var MastraCode = class extends Harness {
|
|
|
1488
1526
|
level: "official",
|
|
1489
1527
|
note: "Global MCP server configurations."
|
|
1490
1528
|
},
|
|
1529
|
+
{
|
|
1530
|
+
path: ".mcp.json",
|
|
1531
|
+
scope: "project",
|
|
1532
|
+
level: "official",
|
|
1533
|
+
note: "Claude Code compatible project MCP servers, read below .mastracode/mcp.json."
|
|
1534
|
+
},
|
|
1491
1535
|
{
|
|
1492
1536
|
path: ".mastracode/hooks.json",
|
|
1493
1537
|
scope: "project",
|
|
@@ -1579,16 +1623,51 @@ var MastraCode = class extends Harness {
|
|
|
1579
1623
|
note: "Claude Code compatible."
|
|
1580
1624
|
}
|
|
1581
1625
|
];
|
|
1582
|
-
commands = [
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1626
|
+
commands = [
|
|
1627
|
+
{
|
|
1628
|
+
path: ".mastracode/commands/",
|
|
1629
|
+
scope: "project",
|
|
1630
|
+
level: "official",
|
|
1631
|
+
note: "Markdown prompt commands; subdirectories become colon-separated command names."
|
|
1632
|
+
},
|
|
1633
|
+
{
|
|
1634
|
+
path: "~/.mastracode/commands/",
|
|
1635
|
+
scope: "user",
|
|
1636
|
+
level: "official",
|
|
1637
|
+
note: "Global Markdown prompt commands."
|
|
1638
|
+
},
|
|
1639
|
+
{
|
|
1640
|
+
path: ".claude/commands/",
|
|
1641
|
+
scope: "project",
|
|
1642
|
+
level: "official",
|
|
1643
|
+
note: "Claude compatibility."
|
|
1644
|
+
},
|
|
1645
|
+
{
|
|
1646
|
+
path: "~/.claude/commands/",
|
|
1647
|
+
scope: "user",
|
|
1648
|
+
level: "official",
|
|
1649
|
+
note: "Claude compatibility."
|
|
1650
|
+
},
|
|
1651
|
+
{
|
|
1652
|
+
path: ".opencode/command/",
|
|
1653
|
+
scope: "project",
|
|
1654
|
+
level: "official",
|
|
1655
|
+
note: "Mastra's OpenCode compatibility loader uses the singular command directory."
|
|
1656
|
+
},
|
|
1657
|
+
{
|
|
1658
|
+
path: "~/.opencode/command/",
|
|
1659
|
+
scope: "user",
|
|
1660
|
+
level: "official",
|
|
1661
|
+
note: "Mastra's OpenCode compatibility loader uses the singular command directory."
|
|
1662
|
+
}
|
|
1663
|
+
];
|
|
1664
|
+
promptTemplates = this.commands;
|
|
1665
|
+
promptTemplateSyncTarget = {
|
|
1587
1666
|
path: "~/.mastracode/commands/",
|
|
1588
1667
|
scope: "user",
|
|
1589
1668
|
level: "official",
|
|
1590
|
-
|
|
1591
|
-
}
|
|
1669
|
+
format: "markdown"
|
|
1670
|
+
};
|
|
1592
1671
|
hooks = [{
|
|
1593
1672
|
path: ".mastracode/hooks.json",
|
|
1594
1673
|
scope: "project",
|
|
@@ -1600,6 +1679,35 @@ var MastraCode = class extends Harness {
|
|
|
1600
1679
|
note: "Global hooks."
|
|
1601
1680
|
}];
|
|
1602
1681
|
invocation = null;
|
|
1682
|
+
mcpConfigs = [
|
|
1683
|
+
{
|
|
1684
|
+
path: "~/.mastracode/mcp.json",
|
|
1685
|
+
scope: "user",
|
|
1686
|
+
level: "official",
|
|
1687
|
+
format: "json",
|
|
1688
|
+
key: ["mcpServers"],
|
|
1689
|
+
dialect: "mastracode",
|
|
1690
|
+
note: "An entry with command is stdio and one with url is HTTP, type is ignored. Loaded above mcpServers from the project's .claude/settings.local.json and below both project files, later names winning."
|
|
1691
|
+
},
|
|
1692
|
+
{
|
|
1693
|
+
path: ".mastracode/mcp.json",
|
|
1694
|
+
scope: "project",
|
|
1695
|
+
level: "official",
|
|
1696
|
+
format: "json",
|
|
1697
|
+
key: ["mcpServers"],
|
|
1698
|
+
dialect: "mastracode",
|
|
1699
|
+
note: "Highest priority, overrides the other files entry by entry."
|
|
1700
|
+
},
|
|
1701
|
+
{
|
|
1702
|
+
path: ".mcp.json",
|
|
1703
|
+
scope: "project",
|
|
1704
|
+
level: "official",
|
|
1705
|
+
format: "json",
|
|
1706
|
+
key: ["mcpServers"],
|
|
1707
|
+
dialect: "mastracode",
|
|
1708
|
+
note: "Claude Code's project file, read between the user file and .mastracode/mcp.json. Project writes go to .mastracode/mcp.json, this one is Claude's to edit."
|
|
1709
|
+
}
|
|
1710
|
+
];
|
|
1603
1711
|
detection = {
|
|
1604
1712
|
envVars: [],
|
|
1605
1713
|
projectMarkers: [".mastracode"]
|
|
@@ -1747,13 +1855,35 @@ var Omp = class extends Harness {
|
|
|
1747
1855
|
commands = [{
|
|
1748
1856
|
path: ".omp/commands/",
|
|
1749
1857
|
scope: "project",
|
|
1750
|
-
level: "official"
|
|
1858
|
+
level: "official",
|
|
1859
|
+
note: "Markdown prompt commands and TypeScript commands; only Markdown files are templates."
|
|
1751
1860
|
}, {
|
|
1752
1861
|
path: "~/.omp/agent/commands/",
|
|
1753
1862
|
scope: "user",
|
|
1754
1863
|
level: "official",
|
|
1755
|
-
note: "Global
|
|
1864
|
+
note: "Global Markdown prompt commands and TypeScript commands; only Markdown files are templates."
|
|
1756
1865
|
}];
|
|
1866
|
+
promptTemplates = [
|
|
1867
|
+
...this.commands,
|
|
1868
|
+
{
|
|
1869
|
+
path: ".omp/prompts/",
|
|
1870
|
+
scope: "project",
|
|
1871
|
+
level: "official",
|
|
1872
|
+
note: "Markdown prompt templates invoked as /name; subdirectories are scanned."
|
|
1873
|
+
},
|
|
1874
|
+
{
|
|
1875
|
+
path: "~/.omp/agent/prompts/",
|
|
1876
|
+
scope: "user",
|
|
1877
|
+
level: "official",
|
|
1878
|
+
note: "Global Markdown prompt templates, separate from executable commands."
|
|
1879
|
+
}
|
|
1880
|
+
];
|
|
1881
|
+
promptTemplateSyncTarget = {
|
|
1882
|
+
path: "~/.omp/agent/prompts/",
|
|
1883
|
+
scope: "user",
|
|
1884
|
+
level: "official",
|
|
1885
|
+
format: "markdown"
|
|
1886
|
+
};
|
|
1757
1887
|
hooks = [];
|
|
1758
1888
|
invocation = {
|
|
1759
1889
|
args: ["-p", "{prompt}"],
|
|
@@ -1896,7 +2026,24 @@ var OpenCode = class extends Harness {
|
|
|
1896
2026
|
level: "official"
|
|
1897
2027
|
}
|
|
1898
2028
|
];
|
|
1899
|
-
commands = [
|
|
2029
|
+
commands = [{
|
|
2030
|
+
path: ".opencode/commands/",
|
|
2031
|
+
scope: "project",
|
|
2032
|
+
level: "official",
|
|
2033
|
+
note: "Markdown prompt commands; also configurable through command entries in opencode.json."
|
|
2034
|
+
}, {
|
|
2035
|
+
path: "~/.config/opencode/commands/",
|
|
2036
|
+
scope: "user",
|
|
2037
|
+
level: "official",
|
|
2038
|
+
note: "Global Markdown prompt commands."
|
|
2039
|
+
}];
|
|
2040
|
+
promptTemplates = this.commands;
|
|
2041
|
+
promptTemplateSyncTarget = {
|
|
2042
|
+
path: "~/.config/opencode/commands/",
|
|
2043
|
+
scope: "user",
|
|
2044
|
+
level: "official",
|
|
2045
|
+
format: "markdown"
|
|
2046
|
+
};
|
|
1900
2047
|
hooks = [];
|
|
1901
2048
|
invocation = {
|
|
1902
2049
|
args: ["run", "{prompt}"],
|
|
@@ -2058,6 +2205,13 @@ var Pi = class extends Harness {
|
|
|
2058
2205
|
level: "official",
|
|
2059
2206
|
note: "Global user-level prompt templates."
|
|
2060
2207
|
}];
|
|
2208
|
+
promptTemplates = this.commands;
|
|
2209
|
+
promptTemplateSyncTarget = {
|
|
2210
|
+
path: "~/.pi/agent/prompts/",
|
|
2211
|
+
scope: "user",
|
|
2212
|
+
level: "official",
|
|
2213
|
+
format: "markdown"
|
|
2214
|
+
};
|
|
2061
2215
|
hooks = [];
|
|
2062
2216
|
invocation = {
|
|
2063
2217
|
args: ["-p", "{prompt}"],
|
|
@@ -2228,6 +2382,13 @@ var PrimeAgent = class extends Harness {
|
|
|
2228
2382
|
level: "official",
|
|
2229
2383
|
note: "Global user-level prompt templates."
|
|
2230
2384
|
}];
|
|
2385
|
+
promptTemplates = this.commands;
|
|
2386
|
+
promptTemplateSyncTarget = {
|
|
2387
|
+
path: "~/.prime/agent/prompts/",
|
|
2388
|
+
scope: "user",
|
|
2389
|
+
level: "official",
|
|
2390
|
+
format: "markdown"
|
|
2391
|
+
};
|
|
2231
2392
|
hooks = [];
|
|
2232
2393
|
invocation = {
|
|
2233
2394
|
args: ["-p", "{prompt}"],
|
|
@@ -2457,8 +2618,21 @@ function normalizePrime(name, raw) {
|
|
|
2457
2618
|
env: primeEnv(raw.env)
|
|
2458
2619
|
});
|
|
2459
2620
|
}
|
|
2621
|
+
function normalizeMastracode(name, raw) {
|
|
2622
|
+
const url = stringField(raw, "url");
|
|
2623
|
+
return compact({
|
|
2624
|
+
name,
|
|
2625
|
+
transport: url ? "http" : "stdio",
|
|
2626
|
+
command: stringField(raw, "command"),
|
|
2627
|
+
args: asStringArray(raw.args),
|
|
2628
|
+
env: asStringRecord(raw.env),
|
|
2629
|
+
url,
|
|
2630
|
+
headers: asStringRecord(raw.headers)
|
|
2631
|
+
});
|
|
2632
|
+
}
|
|
2460
2633
|
function normalizeEntry(name, raw, dialect) {
|
|
2461
2634
|
if (dialect === "antigravity") return normalizeAntigravity(name, raw);
|
|
2635
|
+
if (dialect === "mastracode") return normalizeMastracode(name, raw);
|
|
2462
2636
|
if (dialect === "opencode") return normalizeOpenCode(name, raw);
|
|
2463
2637
|
if (dialect === "prime") return normalizePrime(name, raw);
|
|
2464
2638
|
return normalizeStandard(name, raw);
|
|
@@ -2485,6 +2659,17 @@ function denormalizeOpenCode(server) {
|
|
|
2485
2659
|
enabled: server.enabled
|
|
2486
2660
|
});
|
|
2487
2661
|
}
|
|
2662
|
+
function denormalizeMastracode(server) {
|
|
2663
|
+
if (server.url) return compact({
|
|
2664
|
+
url: server.url,
|
|
2665
|
+
headers: server.headers
|
|
2666
|
+
});
|
|
2667
|
+
return compact({
|
|
2668
|
+
command: server.command,
|
|
2669
|
+
args: server.args?.length ? server.args : void 0,
|
|
2670
|
+
env: server.env
|
|
2671
|
+
});
|
|
2672
|
+
}
|
|
2488
2673
|
function denormalizeStandard(server) {
|
|
2489
2674
|
return compact({
|
|
2490
2675
|
type: server.transport,
|
|
@@ -2530,8 +2715,18 @@ function resolveEnvReferences(server, dialect) {
|
|
|
2530
2715
|
env
|
|
2531
2716
|
};
|
|
2532
2717
|
}
|
|
2718
|
+
function shapeForDialect(server, dialect) {
|
|
2719
|
+
const shaped = resolveEnvReferences(server, dialect);
|
|
2720
|
+
if (dialect !== "mastracode") return shaped;
|
|
2721
|
+
return compact({
|
|
2722
|
+
...shaped,
|
|
2723
|
+
transport: shaped.url ? "http" : "stdio",
|
|
2724
|
+
enabled: void 0
|
|
2725
|
+
});
|
|
2726
|
+
}
|
|
2533
2727
|
function denormalizeEntry(server, dialect) {
|
|
2534
2728
|
if (dialect === "antigravity") return denormalizeAntigravity(server);
|
|
2729
|
+
if (dialect === "mastracode") return denormalizeMastracode(server);
|
|
2535
2730
|
if (dialect === "opencode") return denormalizeOpenCode(server);
|
|
2536
2731
|
if (dialect === "prime") return denormalizePrime(server);
|
|
2537
2732
|
return denormalizeStandard(server);
|
|
@@ -2667,7 +2862,7 @@ function removeTomlServer(path, key, name) {
|
|
|
2667
2862
|
}
|
|
2668
2863
|
function addMcpServer(harness, server, scope = "user", options = {}) {
|
|
2669
2864
|
const { entry, path } = writableConfig(harness, scope, options);
|
|
2670
|
-
const resolved =
|
|
2865
|
+
const resolved = shapeForDialect(server, entry.dialect);
|
|
2671
2866
|
if (entry.format === "toml") return {
|
|
2672
2867
|
path,
|
|
2673
2868
|
replaced: addTomlServer(path, entry.key, resolved)
|
|
@@ -2887,7 +3082,7 @@ function syncIncludedHarness(harness, master, existing, masterNames, initialPath
|
|
|
2887
3082
|
let path = initialPath;
|
|
2888
3083
|
const { dialect } = writableConfig(harness, "user", options).entry;
|
|
2889
3084
|
for (const server of master.servers) try {
|
|
2890
|
-
const resolved =
|
|
3085
|
+
const resolved = shapeForDialect(server, dialect);
|
|
2891
3086
|
if (existing.get(server.name) === canonical(resolved)) {
|
|
2892
3087
|
results.push({
|
|
2893
3088
|
name: server.name,
|
|
@@ -3035,6 +3230,15 @@ function relink(path, source) {
|
|
|
3035
3230
|
symlinkSync(source, temp);
|
|
3036
3231
|
renameSync(temp, path);
|
|
3037
3232
|
}
|
|
3233
|
+
function moveFile(from, to) {
|
|
3234
|
+
try {
|
|
3235
|
+
renameSync(from, to);
|
|
3236
|
+
} catch (error) {
|
|
3237
|
+
if (error.code !== "EXDEV") throw error;
|
|
3238
|
+
copyFileSync(from, to);
|
|
3239
|
+
unlinkSync(from);
|
|
3240
|
+
}
|
|
3241
|
+
}
|
|
3038
3242
|
const CHECK_ACTION = {
|
|
3039
3243
|
missing: "linked",
|
|
3040
3244
|
"correct-link": "unchanged",
|
|
@@ -3047,7 +3251,7 @@ function applyFileTarget(id, path, source, state, options) {
|
|
|
3047
3251
|
const backupDir = join(agntnConfigDir(options), "diverged");
|
|
3048
3252
|
mkdirSync(backupDir, { recursive: true });
|
|
3049
3253
|
const backup = join(backupDir, `${id}-${basename(path)}-${Date.now()}.md`);
|
|
3050
|
-
|
|
3254
|
+
moveFile(path, backup);
|
|
3051
3255
|
relink(path, source);
|
|
3052
3256
|
return {
|
|
3053
3257
|
path,
|
|
@@ -3082,7 +3286,7 @@ function syncFileTarget(id, path, source, check, options) {
|
|
|
3082
3286
|
};
|
|
3083
3287
|
return applyFileTarget(id, path, source, state, options);
|
|
3084
3288
|
}
|
|
3085
|
-
function syncTarget(harness, config, check, options) {
|
|
3289
|
+
function syncTarget$1(harness, config, check, options) {
|
|
3086
3290
|
if (config.excludes.includes(harness.id)) return {
|
|
3087
3291
|
id: harness.id,
|
|
3088
3292
|
action: "skipped",
|
|
@@ -3133,9 +3337,301 @@ function syncAgentsFiles(harnesses, check = false, options = {}) {
|
|
|
3133
3337
|
return {
|
|
3134
3338
|
source: config.source,
|
|
3135
3339
|
check,
|
|
3136
|
-
targets: harnesses.map((harness) => syncTarget(harness, config, check, options))
|
|
3340
|
+
targets: harnesses.map((harness) => syncTarget$1(harness, config, check, options))
|
|
3341
|
+
};
|
|
3342
|
+
}
|
|
3343
|
+
const GENERATED_MARKER = "# Generated by @agntn/harnesses from ";
|
|
3344
|
+
const PORTABLE_PROMPT_NAME = /^[A-Za-z0-9][A-Za-z0-9_-]*$/u;
|
|
3345
|
+
const GEMINI_ACTION = {
|
|
3346
|
+
missing: "generated",
|
|
3347
|
+
"correct-link": "adopted",
|
|
3348
|
+
"wrong-link": "adopted",
|
|
3349
|
+
"owned-file": "replaced",
|
|
3350
|
+
"unmanaged-file": "adopted",
|
|
3351
|
+
unsupported: "skipped"
|
|
3352
|
+
};
|
|
3353
|
+
const GEMINI_BACKUP = {
|
|
3354
|
+
missing: false,
|
|
3355
|
+
"correct-link": true,
|
|
3356
|
+
"wrong-link": true,
|
|
3357
|
+
"owned-file": true,
|
|
3358
|
+
"unmanaged-file": true,
|
|
3359
|
+
unsupported: false
|
|
3360
|
+
};
|
|
3361
|
+
function readTemplates(sourceDir, check) {
|
|
3362
|
+
let entries;
|
|
3363
|
+
try {
|
|
3364
|
+
entries = readdirSync(sourceDir, { withFileTypes: true });
|
|
3365
|
+
} catch (error) {
|
|
3366
|
+
if (error.code !== "ENOENT") throw error;
|
|
3367
|
+
if (!check) mkdirSync(sourceDir, { recursive: true });
|
|
3368
|
+
return [];
|
|
3369
|
+
}
|
|
3370
|
+
const templates = entries.filter((entry) => (entry.isFile() || entry.isSymbolicLink()) && extname(entry.name).toLowerCase() === ".md").map((entry) => {
|
|
3371
|
+
const name = basename(entry.name, extname(entry.name));
|
|
3372
|
+
if (!PORTABLE_PROMPT_NAME.test(name)) throw new TypeError(`Invalid prompt template filename: ${entry.name}`);
|
|
3373
|
+
const path = join(sourceDir, entry.name);
|
|
3374
|
+
return {
|
|
3375
|
+
name,
|
|
3376
|
+
path,
|
|
3377
|
+
content: readFileSync(path, "utf8")
|
|
3378
|
+
};
|
|
3379
|
+
}).sort((left, right) => left.name.localeCompare(right.name));
|
|
3380
|
+
const names = templates.map((template) => template.name.toLowerCase());
|
|
3381
|
+
if (new Set(names).size !== names.length) throw new Error(`Duplicate prompt template names in ${sourceDir}`);
|
|
3382
|
+
return templates;
|
|
3383
|
+
}
|
|
3384
|
+
function generatedSource(content) {
|
|
3385
|
+
const firstLine = content.split("\n", 1)[0] ?? "";
|
|
3386
|
+
if (!firstLine.startsWith(GENERATED_MARKER)) return null;
|
|
3387
|
+
try {
|
|
3388
|
+
const source = JSON.parse(firstLine.slice(37));
|
|
3389
|
+
return typeof source === "string" ? source : null;
|
|
3390
|
+
} catch {
|
|
3391
|
+
return null;
|
|
3392
|
+
}
|
|
3393
|
+
}
|
|
3394
|
+
function inspectDestination(path, source) {
|
|
3395
|
+
let stats;
|
|
3396
|
+
try {
|
|
3397
|
+
stats = lstatSync(path);
|
|
3398
|
+
} catch (error) {
|
|
3399
|
+
if (error.code === "ENOENT") return { kind: "missing" };
|
|
3400
|
+
throw error;
|
|
3401
|
+
}
|
|
3402
|
+
if (stats.isSymbolicLink()) {
|
|
3403
|
+
if (source === void 0) return { kind: "unmanaged-file" };
|
|
3404
|
+
const target = readlinkSync(path);
|
|
3405
|
+
return (isAbsolute(target) ? target : resolve(dirname(path), target)) === source ? { kind: "correct-link" } : { kind: "wrong-link" };
|
|
3406
|
+
}
|
|
3407
|
+
if (!stats.isFile()) return { kind: "unsupported" };
|
|
3408
|
+
const content = readFileSync(path, "utf8");
|
|
3409
|
+
if (generatedSource(content) !== null) return {
|
|
3410
|
+
kind: "owned-file",
|
|
3411
|
+
content
|
|
3412
|
+
};
|
|
3413
|
+
return { kind: "unmanaged-file" };
|
|
3414
|
+
}
|
|
3415
|
+
function tempPath(path, kind) {
|
|
3416
|
+
return join(dirname(path), `.${basename(path)}-${randomUUID()}.${kind}.tmp`);
|
|
3417
|
+
}
|
|
3418
|
+
function linkTemplate(path, source) {
|
|
3419
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
3420
|
+
const temp = tempPath(path, "prompt-link");
|
|
3421
|
+
symlinkSync(source, temp);
|
|
3422
|
+
renameSync(temp, path);
|
|
3423
|
+
}
|
|
3424
|
+
function writeTemplate(path, content) {
|
|
3425
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
3426
|
+
const temp = tempPath(path, "prompt-write");
|
|
3427
|
+
writeFileSync(temp, content);
|
|
3428
|
+
renameSync(temp, path);
|
|
3429
|
+
}
|
|
3430
|
+
function backupDestination(id, path, options) {
|
|
3431
|
+
const backupDir = join(agntnDataDir(options), "diverged", "prompts", id);
|
|
3432
|
+
mkdirSync(backupDir, { recursive: true });
|
|
3433
|
+
const backup = join(backupDir, `${basename(path)}-${Date.now()}-${randomUUID()}`);
|
|
3434
|
+
if (lstatSync(path).isSymbolicLink()) {
|
|
3435
|
+
const target = readlinkSync(path);
|
|
3436
|
+
symlinkSync(isAbsolute(target) ? target : resolve(dirname(path), target), backup);
|
|
3437
|
+
unlinkSync(path);
|
|
3438
|
+
return backup;
|
|
3439
|
+
}
|
|
3440
|
+
moveFile(path, backup);
|
|
3441
|
+
return backup;
|
|
3442
|
+
}
|
|
3443
|
+
function checkDetail() {
|
|
3444
|
+
return "check mode: not applied";
|
|
3445
|
+
}
|
|
3446
|
+
function syncMarkdownTemplate(id, targetDir, template, check, options) {
|
|
3447
|
+
const path = join(targetDir, `${template.name}.md`);
|
|
3448
|
+
const state = inspectDestination(path, template.path);
|
|
3449
|
+
if (state.kind === "correct-link") return {
|
|
3450
|
+
name: template.name,
|
|
3451
|
+
source: template.path,
|
|
3452
|
+
path,
|
|
3453
|
+
action: "unchanged"
|
|
3454
|
+
};
|
|
3455
|
+
if (state.kind === "unsupported") return {
|
|
3456
|
+
name: template.name,
|
|
3457
|
+
source: template.path,
|
|
3458
|
+
path,
|
|
3459
|
+
action: "skipped",
|
|
3460
|
+
detail: "destination exists and is not a file or symlink"
|
|
3461
|
+
};
|
|
3462
|
+
const action = state.kind === "missing" ? "linked" : state.kind === "wrong-link" ? "relinked" : "adopted";
|
|
3463
|
+
if (check) return {
|
|
3464
|
+
name: template.name,
|
|
3465
|
+
source: template.path,
|
|
3466
|
+
path,
|
|
3467
|
+
action,
|
|
3468
|
+
detail: checkDetail()
|
|
3469
|
+
};
|
|
3470
|
+
let detail;
|
|
3471
|
+
if (state.kind === "wrong-link") unlinkSync(path);
|
|
3472
|
+
if (state.kind === "owned-file" || state.kind === "unmanaged-file") detail = backupDestination(id, path, options);
|
|
3473
|
+
linkTemplate(path, template.path);
|
|
3474
|
+
return {
|
|
3475
|
+
name: template.name,
|
|
3476
|
+
source: template.path,
|
|
3477
|
+
path,
|
|
3478
|
+
action,
|
|
3479
|
+
detail
|
|
3480
|
+
};
|
|
3481
|
+
}
|
|
3482
|
+
function frontmatterDescription(raw, path) {
|
|
3483
|
+
const frontmatter = parse$1(raw);
|
|
3484
|
+
if (frontmatter === null) return void 0;
|
|
3485
|
+
if (typeof frontmatter !== "object" || Array.isArray(frontmatter)) throw new TypeError(`Prompt template frontmatter is not an object: ${path}`);
|
|
3486
|
+
const description = frontmatter.description;
|
|
3487
|
+
if (description !== void 0 && typeof description !== "string") throw new TypeError(`Prompt template description is not a string: ${path}`);
|
|
3488
|
+
return description;
|
|
3489
|
+
}
|
|
3490
|
+
function parseMarkdownTemplate(template) {
|
|
3491
|
+
const match = /^---\r?\n([\s\S]*?)\r?\n---(?:\r?\n|$)/.exec(template.content);
|
|
3492
|
+
if (match === null) return { prompt: template.content.replaceAll("$ARGUMENTS", "{{args}}") };
|
|
3493
|
+
const description = frontmatterDescription(match[1] ?? "", template.path);
|
|
3494
|
+
return {
|
|
3495
|
+
...description === void 0 ? {} : { description },
|
|
3496
|
+
prompt: template.content.slice(match[0].length).replaceAll("$ARGUMENTS", "{{args}}")
|
|
3497
|
+
};
|
|
3498
|
+
}
|
|
3499
|
+
function geminiContent(template) {
|
|
3500
|
+
const parsed = parseMarkdownTemplate(template);
|
|
3501
|
+
return [
|
|
3502
|
+
`${GENERATED_MARKER}${JSON.stringify(template.path)}`,
|
|
3503
|
+
...parsed.description === void 0 ? [] : [`description = ${JSON.stringify(parsed.description)}`],
|
|
3504
|
+
`prompt = ${JSON.stringify(parsed.prompt)}`,
|
|
3505
|
+
""
|
|
3506
|
+
].join("\n");
|
|
3507
|
+
}
|
|
3508
|
+
function syncGeminiTemplate(id, targetDir, template, check, options) {
|
|
3509
|
+
const path = join(targetDir, `${template.name}.toml`);
|
|
3510
|
+
const content = template.gemini ?? geminiContent(template);
|
|
3511
|
+
const state = inspectDestination(path);
|
|
3512
|
+
if (state.kind === "owned-file" && state.content === content) return {
|
|
3513
|
+
name: template.name,
|
|
3514
|
+
source: template.path,
|
|
3515
|
+
path,
|
|
3516
|
+
action: "unchanged"
|
|
3517
|
+
};
|
|
3518
|
+
if (state.kind === "unsupported") return {
|
|
3519
|
+
name: template.name,
|
|
3520
|
+
source: template.path,
|
|
3521
|
+
path,
|
|
3522
|
+
action: "skipped",
|
|
3523
|
+
detail: "destination exists and is not a file or symlink"
|
|
3524
|
+
};
|
|
3525
|
+
const action = GEMINI_ACTION[state.kind];
|
|
3526
|
+
if (check) return {
|
|
3527
|
+
name: template.name,
|
|
3528
|
+
source: template.path,
|
|
3529
|
+
path,
|
|
3530
|
+
action,
|
|
3531
|
+
detail: checkDetail()
|
|
3532
|
+
};
|
|
3533
|
+
let detail;
|
|
3534
|
+
if (GEMINI_BACKUP[state.kind]) detail = backupDestination(id, path, options);
|
|
3535
|
+
writeTemplate(path, content);
|
|
3536
|
+
return {
|
|
3537
|
+
name: template.name,
|
|
3538
|
+
source: template.path,
|
|
3539
|
+
path,
|
|
3540
|
+
action,
|
|
3541
|
+
detail
|
|
3542
|
+
};
|
|
3543
|
+
}
|
|
3544
|
+
function isInsideDirectory(path, directory) {
|
|
3545
|
+
const remainder = relative(directory, path);
|
|
3546
|
+
return remainder !== "" && !remainder.startsWith("..") && !isAbsolute(remainder);
|
|
3547
|
+
}
|
|
3548
|
+
function staleMarkdownSource(path, sourceDir) {
|
|
3549
|
+
if (inspectDestination(path).kind !== "unmanaged-file") return null;
|
|
3550
|
+
let stats;
|
|
3551
|
+
try {
|
|
3552
|
+
stats = lstatSync(path);
|
|
3553
|
+
} catch {
|
|
3554
|
+
return null;
|
|
3555
|
+
}
|
|
3556
|
+
if (!stats.isSymbolicLink()) return null;
|
|
3557
|
+
const target = readlinkSync(path);
|
|
3558
|
+
const resolved = isAbsolute(target) ? target : resolve(dirname(path), target);
|
|
3559
|
+
return isInsideDirectory(resolved, sourceDir) ? resolved : null;
|
|
3560
|
+
}
|
|
3561
|
+
function staleGeminiSource(path, sourceDir) {
|
|
3562
|
+
let content;
|
|
3563
|
+
try {
|
|
3564
|
+
content = readFileSync(path, "utf8");
|
|
3565
|
+
} catch {
|
|
3566
|
+
return null;
|
|
3567
|
+
}
|
|
3568
|
+
const source = generatedSource(content);
|
|
3569
|
+
if (source === null) return null;
|
|
3570
|
+
return isInsideDirectory(source, sourceDir) ? source : null;
|
|
3571
|
+
}
|
|
3572
|
+
function pruneTarget(targetDir, format, sourceDir, activeSources, check) {
|
|
3573
|
+
let entries;
|
|
3574
|
+
try {
|
|
3575
|
+
entries = readdirSync(targetDir, { withFileTypes: true });
|
|
3576
|
+
} catch (error) {
|
|
3577
|
+
if (error.code === "ENOENT") return [];
|
|
3578
|
+
throw error;
|
|
3579
|
+
}
|
|
3580
|
+
const extension = format === "markdown" ? ".md" : ".toml";
|
|
3581
|
+
const staleSource = format === "markdown" ? staleMarkdownSource : staleGeminiSource;
|
|
3582
|
+
return entries.filter((entry) => entry.name.toLowerCase().endsWith(extension)).flatMap((entry) => {
|
|
3583
|
+
const path = join(targetDir, entry.name);
|
|
3584
|
+
const source = staleSource(path, sourceDir);
|
|
3585
|
+
if (source === null || activeSources.has(source)) return [];
|
|
3586
|
+
if (!check) unlinkSync(path);
|
|
3587
|
+
return [{
|
|
3588
|
+
name: basename(entry.name, extension),
|
|
3589
|
+
source,
|
|
3590
|
+
path,
|
|
3591
|
+
action: "removed",
|
|
3592
|
+
...check ? { detail: checkDetail() } : {}
|
|
3593
|
+
}];
|
|
3594
|
+
});
|
|
3595
|
+
}
|
|
3596
|
+
function syncTarget(harness, templates, sourceDir, check, options) {
|
|
3597
|
+
const target = harness.promptTemplateSyncTarget;
|
|
3598
|
+
if (target === null) return {
|
|
3599
|
+
id: harness.id,
|
|
3600
|
+
detail: "no stable user-scope prompt template destination",
|
|
3601
|
+
templates: []
|
|
3602
|
+
};
|
|
3603
|
+
const resolved = harness.resolveCandidates([target], options)[0];
|
|
3604
|
+
if (resolved === void 0) return {
|
|
3605
|
+
id: harness.id,
|
|
3606
|
+
detail: "prompt template destination is unavailable on this platform",
|
|
3607
|
+
templates: []
|
|
3608
|
+
};
|
|
3609
|
+
const targetDir = resolved.path;
|
|
3610
|
+
const syncTemplate = resolved.format === "markdown" ? syncMarkdownTemplate : syncGeminiTemplate;
|
|
3611
|
+
const results = templates.map((template) => syncTemplate(harness.id, targetDir, template, check, options));
|
|
3612
|
+
const activeSources = new Set(templates.map((template) => template.path));
|
|
3613
|
+
results.push(...pruneTarget(targetDir, resolved.format, sourceDir, activeSources, check));
|
|
3614
|
+
return {
|
|
3615
|
+
id: harness.id,
|
|
3616
|
+
path: targetDir,
|
|
3617
|
+
format: resolved.format,
|
|
3618
|
+
templates: results
|
|
3619
|
+
};
|
|
3620
|
+
}
|
|
3621
|
+
function syncPromptTemplates(harnesses, check = false, options = {}) {
|
|
3622
|
+
const source = join(agntnDataDir(options), "prompts");
|
|
3623
|
+
const templates = readTemplates(source, check);
|
|
3624
|
+
const prepared = harnesses.some((harness) => harness.promptTemplateSyncTarget?.format === "gemini-toml") ? templates.map((template) => ({
|
|
3625
|
+
...template,
|
|
3626
|
+
gemini: geminiContent(template)
|
|
3627
|
+
})) : templates;
|
|
3628
|
+
return {
|
|
3629
|
+
source,
|
|
3630
|
+
check,
|
|
3631
|
+
templates: prepared.map((template) => template.name),
|
|
3632
|
+
targets: harnesses.map((harness) => syncTarget(harness, prepared, source, check, options))
|
|
3137
3633
|
};
|
|
3138
3634
|
}
|
|
3139
|
-
export { Harness, McpEnvError, addMcpServer, detectHarness, detectHarnessFromEnv, detectProjectHarnesses, getAllHarnesses, getHarness, isHarnessId, listHarnesses, listMcpServers, masterMcpPath, parseJsonc, readAgentsConfig, readMasterMcpServers, registerHarness, removeMcpServer, resolvePathTemplate, syncAgentsFiles, syncMcpServers };
|
|
3635
|
+
export { Harness, McpEnvError, addMcpServer, agntnConfigDir, agntnDataDir, detectHarness, detectHarnessFromEnv, detectProjectHarnesses, getAllHarnesses, getHarness, isHarnessId, listHarnesses, listMcpServers, masterMcpPath, parseJsonc, readAgentsConfig, readMasterMcpServers, registerHarness, removeMcpServer, resolvePathTemplate, syncAgentsFiles, syncMcpServers, syncPromptTemplates };
|
|
3140
3636
|
|
|
3141
|
-
//# sourceMappingURL=
|
|
3637
|
+
//# sourceMappingURL=prompt-sync.mjs.map
|