@agntn/harnesses 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +47 -15
- package/dist/_chunks/agents.mjs +2 -30
- package/dist/_chunks/agents.mjs.map +1 -1
- package/dist/_chunks/libs/typebox.mjs +12 -9
- 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} +122 -10
- package/dist/_chunks/prompt-sync.d.mts.map +1 -0
- package/dist/_chunks/{agents-sync.mjs → prompt-sync.mjs} +863 -50
- 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 +57 -19
- package/dist/_chunks/tool-operations.mjs.map +1 -1
- package/dist/_chunks/tool-schemas.d.mts +6 -0
- package/dist/_chunks/tool-schemas.d.mts.map +1 -1
- package/dist/_chunks/tool-schemas.mjs +14 -9
- 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 +26 -8
- 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 +179 -1
- package/dist/mcp.mjs.map +1 -0
- package/dist/tool-operations.d.mts +19 -3
- 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,18 +1,20 @@
|
|
|
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, cpSync, existsSync, lstatSync, mkdirSync, readFileSync, readdirSync, readlinkSync, realpathSync, renameSync, rmSync, rmdirSync, statSync, 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
|
-
|
|
10
|
+
import { randomUUID } from "node:crypto";
|
|
11
|
+
const PATH_MARKERS = /^~(?=\/|$)|\$\{HOME\}|\$\{PROJECT_ROOT\}|\$\{TMPDIR\}|%([^%]+)%/g;
|
|
11
12
|
function resolvePathTemplate(template, options = {}) {
|
|
12
13
|
const homeDir = options.homeDir ?? os.homedir();
|
|
13
14
|
const projectRoot = options.projectRoot ?? process.cwd();
|
|
14
15
|
return template.replaceAll(PATH_MARKERS, (match, name) => {
|
|
15
16
|
if (match === "${PROJECT_ROOT}") return projectRoot;
|
|
17
|
+
if (match === "${TMPDIR}") return options.tempDir ?? os.tmpdir();
|
|
16
18
|
if (name === void 0) return homeDir;
|
|
17
19
|
return process.env[name] ?? match;
|
|
18
20
|
});
|
|
@@ -21,6 +23,15 @@ 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
|
+
}
|
|
30
|
+
function moveRoot(path, root, value) {
|
|
31
|
+
if (path === root) return value;
|
|
32
|
+
const rest = path.slice(root.length);
|
|
33
|
+
return path.startsWith(root) && (rest.startsWith("/") || rest.startsWith("\\")) ? value + rest : path;
|
|
34
|
+
}
|
|
24
35
|
const SUPPORTED_PLATFORMS = {
|
|
25
36
|
linux: true,
|
|
26
37
|
darwin: true,
|
|
@@ -51,6 +62,17 @@ function requestedInvocationMode(options) {
|
|
|
51
62
|
if (options.tools === true) return options.structured === true ? "agentStructured" : "agent";
|
|
52
63
|
return options.structured === true ? "advisorStructured" : "advisor";
|
|
53
64
|
}
|
|
65
|
+
function accessConflictError(id, invocation, options) {
|
|
66
|
+
if (options.readOnly !== true || options.tools !== false) return null;
|
|
67
|
+
const retries = [];
|
|
68
|
+
if (invocationTemplate(invocation, requestedInvocationMode(options))) retries.push("tools: true to keep its read-only tools");
|
|
69
|
+
if (invocationTemplate(invocation, requestedInvocationMode({
|
|
70
|
+
...options,
|
|
71
|
+
readOnly: false
|
|
72
|
+
}))) retries.push("readOnly: false to use its advisor without tools");
|
|
73
|
+
const error = `Harness ${id} cannot run readOnly with tools: false, since read-only access still uses tools`;
|
|
74
|
+
return retries.length === 0 ? error : `${error}; retry with ${retries.join(", or with ")}`;
|
|
75
|
+
}
|
|
54
76
|
function invocationTemplate(invocation, mode) {
|
|
55
77
|
switch (mode) {
|
|
56
78
|
case "advisor": return invocation.noToolsArgs;
|
|
@@ -117,11 +139,29 @@ async function terminateCommand(pid) {
|
|
|
117
139
|
await setTimeout$1(500);
|
|
118
140
|
signalProcessGroup(pid, "SIGKILL");
|
|
119
141
|
}
|
|
142
|
+
function spawnError(command, cwd, error) {
|
|
143
|
+
if (!(error instanceof Error && "code" in error)) return error;
|
|
144
|
+
if (error.code !== "ENOENT" && error.code !== "ENOTDIR") return error;
|
|
145
|
+
let message;
|
|
146
|
+
if (cwd !== void 0 && !isDirectory(cwd)) message = existsSync(cwd) ? `Working directory is not a directory: ${cwd}` : `Working directory not found: ${cwd}`;
|
|
147
|
+
else if (error.code === "ENOENT") message = `Command not found: ${command}. Install it or add it to PATH`;
|
|
148
|
+
else return error;
|
|
149
|
+
return Object.assign(new Error(message, { cause: error }), { code: error.code });
|
|
150
|
+
}
|
|
151
|
+
function isDirectory(path) {
|
|
152
|
+
try {
|
|
153
|
+
return statSync(path).isDirectory();
|
|
154
|
+
} catch {
|
|
155
|
+
return false;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
120
158
|
function executeCommand(command, args, options) {
|
|
121
159
|
return new Promise((resolve, reject) => {
|
|
122
160
|
let stdout = "";
|
|
123
161
|
let stderr = "";
|
|
124
162
|
let stopped;
|
|
163
|
+
let lastOutputAt = performance.now();
|
|
164
|
+
let idleMs;
|
|
125
165
|
let settled = false;
|
|
126
166
|
let timer;
|
|
127
167
|
let abortListener;
|
|
@@ -130,28 +170,38 @@ function executeCommand(command, args, options) {
|
|
|
130
170
|
finish(null);
|
|
131
171
|
return;
|
|
132
172
|
}
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
173
|
+
let child;
|
|
174
|
+
try {
|
|
175
|
+
child = spawn(command, args, {
|
|
176
|
+
cwd: options.cwd,
|
|
177
|
+
env: options.env ? {
|
|
178
|
+
...process.env,
|
|
179
|
+
...options.env
|
|
180
|
+
} : process.env,
|
|
181
|
+
stdio: [
|
|
182
|
+
"ignore",
|
|
183
|
+
"pipe",
|
|
184
|
+
"pipe"
|
|
185
|
+
],
|
|
186
|
+
detached: process.platform !== "win32" && (Boolean(options.timeoutMs) || options.signal !== void 0)
|
|
187
|
+
});
|
|
188
|
+
} catch (error) {
|
|
189
|
+
fail(spawnError(command, options.cwd, error));
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
146
192
|
if (options.timeoutMs) timer = setTimeout(() => stop("timeout"), options.timeoutMs);
|
|
147
193
|
if (options.signal) abortListener = addAbortListener(options.signal, () => stop("abort"));
|
|
194
|
+
child.stdout.setEncoding("utf8");
|
|
195
|
+
child.stderr.setEncoding("utf8");
|
|
148
196
|
child.stdout.on("data", (chunk) => {
|
|
149
|
-
stdout += chunk
|
|
197
|
+
stdout += chunk;
|
|
198
|
+
lastOutputAt = performance.now();
|
|
150
199
|
});
|
|
151
200
|
child.stderr.on("data", (chunk) => {
|
|
152
|
-
stderr += chunk
|
|
201
|
+
stderr += chunk;
|
|
202
|
+
lastOutputAt = performance.now();
|
|
153
203
|
});
|
|
154
|
-
child.on("error", fail);
|
|
204
|
+
child.on("error", (error) => fail(spawnError(command, options.cwd, error)));
|
|
155
205
|
child.on("close", (code) => {
|
|
156
206
|
if (stopped === void 0) finish(code);
|
|
157
207
|
});
|
|
@@ -168,6 +218,7 @@ function executeCommand(command, args, options) {
|
|
|
168
218
|
function stop(reason) {
|
|
169
219
|
if (settled || stopped !== void 0) return;
|
|
170
220
|
stopped = reason;
|
|
221
|
+
idleMs = Math.round(performance.now() - lastOutputAt);
|
|
171
222
|
cleanup();
|
|
172
223
|
if (child.pid === void 0) return;
|
|
173
224
|
terminateCommand(child.pid).then(() => {
|
|
@@ -191,12 +242,17 @@ function executeCommand(command, args, options) {
|
|
|
191
242
|
stderr,
|
|
192
243
|
exitCode: stopped === void 0 ? code : null,
|
|
193
244
|
timedOut: stopped === "timeout",
|
|
194
|
-
aborted: stopped === "abort"
|
|
245
|
+
aborted: stopped === "abort",
|
|
246
|
+
...idleMs === void 0 ? {} : { idleMs }
|
|
195
247
|
});
|
|
196
248
|
}
|
|
197
249
|
});
|
|
198
250
|
}
|
|
199
251
|
var Harness = class {
|
|
252
|
+
promptTemplates = [];
|
|
253
|
+
promptTemplateSyncTarget = null;
|
|
254
|
+
temp = [];
|
|
255
|
+
envOverrides = [];
|
|
200
256
|
modelListing = null;
|
|
201
257
|
mcpConfigs = [];
|
|
202
258
|
agentsFile = null;
|
|
@@ -250,6 +306,7 @@ var Harness = class {
|
|
|
250
306
|
const command = this.invocation.binary ?? this.binaries[0];
|
|
251
307
|
if (!command) return null;
|
|
252
308
|
if (options.model !== void 0 && !this.invocation.modelArgs) return null;
|
|
309
|
+
if (accessConflictError(this.id, this.invocation, options) !== null) return null;
|
|
253
310
|
const template = invocationTemplate(this.invocation, requestedInvocationMode(options));
|
|
254
311
|
if (!template) return null;
|
|
255
312
|
return {
|
|
@@ -260,6 +317,8 @@ var Harness = class {
|
|
|
260
317
|
invocationError(options = {}) {
|
|
261
318
|
if (!this.invocation) return `Harness ${this.id} has no non-interactive invocation`;
|
|
262
319
|
if (options.model !== void 0 && !this.invocation.modelArgs) return `Harness ${this.id} does not support model selection`;
|
|
320
|
+
const conflict = accessConflictError(this.id, this.invocation, options);
|
|
321
|
+
if (conflict !== null) return conflict;
|
|
263
322
|
const mode = requestedInvocationMode(options);
|
|
264
323
|
if (invocationTemplate(this.invocation, mode)) return null;
|
|
265
324
|
return `${`Harness ${this.id} has no ${INVOCATION_MODE_DESCRIPTION[mode]} invocation`}${invocationRetryHint(this.invocation, mode, options)}`;
|
|
@@ -275,7 +334,15 @@ var Harness = class {
|
|
|
275
334
|
if (!built) return Promise.reject(new Error(this.invocationError(invocationOptions) ?? "Invalid invocation"));
|
|
276
335
|
const versionError = this.readOnlyVersionError(invocationOptions);
|
|
277
336
|
if (versionError) return Promise.reject(new Error(versionError));
|
|
278
|
-
|
|
337
|
+
const streamArgs = options.structured ? void 0 : this.invocation?.streamArgs;
|
|
338
|
+
if (!streamArgs) return executeCommand(built.command, built.args, options);
|
|
339
|
+
return executeCommand(built.command, [...built.args, ...streamArgs], options).then((result) => ({
|
|
340
|
+
...result,
|
|
341
|
+
stdout: this.foldStreamOutput(result.stdout, !result.timedOut && !result.aborted)
|
|
342
|
+
}));
|
|
343
|
+
}
|
|
344
|
+
foldStreamOutput(_stdout, _complete) {
|
|
345
|
+
throw new Error(`Harness ${this.id} does not implement stream output folding`);
|
|
279
346
|
}
|
|
280
347
|
readOnlyVersionError(options) {
|
|
281
348
|
const floor = this.invocation?.readOnlyMinVersion;
|
|
@@ -290,22 +357,33 @@ var Harness = class {
|
|
|
290
357
|
if (!this.modelListing) return null;
|
|
291
358
|
const command = this.binaries[0];
|
|
292
359
|
if (!command) return null;
|
|
293
|
-
const template = search === void 0 ? this.modelListing.args : this.modelListing.searchArgs;
|
|
294
|
-
if (!template) return null;
|
|
295
360
|
return {
|
|
296
361
|
command,
|
|
297
|
-
args:
|
|
362
|
+
args: (search === void 0 ? this.modelListing.args : this.modelListing.searchArgs ?? this.modelListing.args).map((arg) => arg.replaceAll("{search}", () => search ?? ""))
|
|
298
363
|
};
|
|
299
364
|
}
|
|
300
365
|
async listModels(options = {}) {
|
|
301
366
|
const built = this.buildModelListInvocation(options.search);
|
|
302
|
-
if (!built) throw new Error(
|
|
367
|
+
if (!built) throw new Error(`Harness ${this.id} does not support model listing`);
|
|
303
368
|
const result = await executeCommand(built.command, built.args, options);
|
|
369
|
+
if (result.timedOut || result.aborted || result.exitCode !== 0) return {
|
|
370
|
+
...result,
|
|
371
|
+
models: []
|
|
372
|
+
};
|
|
373
|
+
const models = this.parseModelListingOutput(result.stdout);
|
|
374
|
+
if (options.search === void 0 || this.modelListing?.searchArgs) return {
|
|
375
|
+
...result,
|
|
376
|
+
models
|
|
377
|
+
};
|
|
378
|
+
const search = options.search.toLowerCase();
|
|
304
379
|
return {
|
|
305
380
|
...result,
|
|
306
|
-
models:
|
|
381
|
+
models: models.filter((model) => model.id.toLowerCase().includes(search))
|
|
307
382
|
};
|
|
308
383
|
}
|
|
384
|
+
modelSelector(model) {
|
|
385
|
+
return `${model.provider}/${model.id}`;
|
|
386
|
+
}
|
|
309
387
|
parseModelListingOutput(_stdout) {
|
|
310
388
|
throw new Error(`Harness ${this.id} does not implement model-list output parsing`);
|
|
311
389
|
}
|
|
@@ -325,10 +403,35 @@ var Harness = class {
|
|
|
325
403
|
instructions: this.resolveCandidates(this.instructions, options),
|
|
326
404
|
skills: this.resolveCandidates(this.skills, options),
|
|
327
405
|
commands: this.resolveCandidates(this.commands, options),
|
|
328
|
-
|
|
406
|
+
promptTemplates: this.resolveCandidates(this.promptTemplates, options),
|
|
407
|
+
promptTemplateSyncTarget: this.promptTemplateSyncTarget === null ? null : this.resolveCandidates([this.promptTemplateSyncTarget], options)[0] ?? null,
|
|
408
|
+
hooks: this.resolveCandidates(this.hooks, options),
|
|
409
|
+
temp: this.resolveTemp(options)
|
|
329
410
|
};
|
|
330
411
|
}
|
|
412
|
+
resolveTemp(options) {
|
|
413
|
+
const temp = this.resolveCandidates(this.temp, options);
|
|
414
|
+
const override = this.resolveCandidates(this.envOverrides, options).find((entry) => entry.relocates.includes("temp"));
|
|
415
|
+
const value = override ? process.env[override.variable] : void 0;
|
|
416
|
+
if (!override || !value) return temp;
|
|
417
|
+
return temp.map((entry) => ({
|
|
418
|
+
...entry,
|
|
419
|
+
path: moveRoot(entry.path, override.path, value)
|
|
420
|
+
}));
|
|
421
|
+
}
|
|
331
422
|
};
|
|
423
|
+
function parseAntigravityModels(stdout) {
|
|
424
|
+
const lines = stripVTControlCharacters(stdout).split(/\r?\n/).map((line) => line.trim()).filter(Boolean);
|
|
425
|
+
if (lines.length === 0) throw new Error("Unexpected empty Antigravity model-list output");
|
|
426
|
+
return lines.map((line) => {
|
|
427
|
+
const match = /^(\S+)\t.+$/.exec(line);
|
|
428
|
+
if (!match?.[1]) throw new Error(`Unexpected Antigravity model-list row: ${JSON.stringify(line)}`);
|
|
429
|
+
return {
|
|
430
|
+
provider: "google",
|
|
431
|
+
id: match[1]
|
|
432
|
+
};
|
|
433
|
+
});
|
|
434
|
+
}
|
|
332
435
|
var Antigravity = class extends Harness {
|
|
333
436
|
id = "antigravity";
|
|
334
437
|
name = "Google Antigravity CLI";
|
|
@@ -472,6 +575,11 @@ var Antigravity = class extends Harness {
|
|
|
472
575
|
level: "official",
|
|
473
576
|
note: "Non-interactive print mode."
|
|
474
577
|
};
|
|
578
|
+
modelListing = {
|
|
579
|
+
args: ["models"],
|
|
580
|
+
level: "official",
|
|
581
|
+
note: "Prints one id and display name per row, with no default marked and no filter; verified with 1.2.5."
|
|
582
|
+
};
|
|
475
583
|
mcpConfigs = [{
|
|
476
584
|
path: "~/.gemini/config/mcp_config.json",
|
|
477
585
|
scope: "user",
|
|
@@ -485,7 +593,60 @@ var Antigravity = class extends Harness {
|
|
|
485
593
|
envVars: [],
|
|
486
594
|
projectMarkers: [".antigravitycli"]
|
|
487
595
|
};
|
|
596
|
+
parseModelListingOutput(stdout) {
|
|
597
|
+
return parseAntigravityModels(stdout);
|
|
598
|
+
}
|
|
599
|
+
modelSelector(model) {
|
|
600
|
+
return model.id;
|
|
601
|
+
}
|
|
488
602
|
};
|
|
603
|
+
function parseStreamLine(line) {
|
|
604
|
+
try {
|
|
605
|
+
const parsed = JSON.parse(line);
|
|
606
|
+
return typeof parsed === "object" && parsed !== null && "type" in parsed && typeof parsed.type === "string" ? parsed : void 0;
|
|
607
|
+
} catch {
|
|
608
|
+
return;
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
function startsTextBlock(event) {
|
|
612
|
+
return event.type === "stream_event" && event.event?.type === "content_block_start" && event.event.content_block?.type === "text";
|
|
613
|
+
}
|
|
614
|
+
function textDelta(event) {
|
|
615
|
+
if (event.type !== "stream_event" || event.event?.type !== "content_block_delta") return void 0;
|
|
616
|
+
const delta = event.event.delta;
|
|
617
|
+
return delta?.type === "text_delta" && typeof delta.text === "string" ? delta.text : void 0;
|
|
618
|
+
}
|
|
619
|
+
var ClaudeStreamFold = class {
|
|
620
|
+
blocks = [];
|
|
621
|
+
other = [];
|
|
622
|
+
result;
|
|
623
|
+
add(line) {
|
|
624
|
+
if (line.trim().length === 0) return;
|
|
625
|
+
const event = parseStreamLine(line);
|
|
626
|
+
if (!event) {
|
|
627
|
+
this.other.push(line);
|
|
628
|
+
return;
|
|
629
|
+
}
|
|
630
|
+
if (event.type === "result" && typeof event.result === "string") this.result = event.result;
|
|
631
|
+
if (startsTextBlock(event)) this.blocks.push("");
|
|
632
|
+
const text = textDelta(event);
|
|
633
|
+
if (text !== void 0) this.blocks.push((this.blocks.pop() ?? "") + text);
|
|
634
|
+
}
|
|
635
|
+
text(complete) {
|
|
636
|
+
const prefix = this.other.map((line) => `${line}\n`).join("");
|
|
637
|
+
if (complete && this.result !== void 0) return `${prefix}${this.result}\n`;
|
|
638
|
+
const text = this.blocks.filter((block) => block.length > 0).join("\n\n");
|
|
639
|
+
return text.length > 0 ? `${prefix}${text}\n` : prefix;
|
|
640
|
+
}
|
|
641
|
+
};
|
|
642
|
+
function foldClaudeStream(stdout, complete) {
|
|
643
|
+
const lines = stdout.split("\n");
|
|
644
|
+
const tail = lines.at(-1);
|
|
645
|
+
if (!complete && tail?.startsWith("{") && parseStreamLine(tail) === void 0) lines.pop();
|
|
646
|
+
const fold = new ClaudeStreamFold();
|
|
647
|
+
for (const line of lines) fold.add(line);
|
|
648
|
+
return fold.text(complete);
|
|
649
|
+
}
|
|
489
650
|
var Claude = class extends Harness {
|
|
490
651
|
id = "claude";
|
|
491
652
|
name = "Anthropic Claude Code";
|
|
@@ -602,6 +763,13 @@ var Claude = class extends Harness {
|
|
|
602
763
|
level: "official",
|
|
603
764
|
note: "Legacy global slash commands."
|
|
604
765
|
}];
|
|
766
|
+
promptTemplates = this.commands;
|
|
767
|
+
promptTemplateSyncTarget = {
|
|
768
|
+
path: "~/.claude/commands/",
|
|
769
|
+
scope: "user",
|
|
770
|
+
level: "official",
|
|
771
|
+
format: "markdown"
|
|
772
|
+
};
|
|
605
773
|
hooks = [{
|
|
606
774
|
path: ".claude/hooks/",
|
|
607
775
|
scope: "project",
|
|
@@ -623,6 +791,7 @@ var Claude = class extends Harness {
|
|
|
623
791
|
noToolsArgs: [
|
|
624
792
|
"-p",
|
|
625
793
|
"{prompt}",
|
|
794
|
+
"--strict-mcp-config",
|
|
626
795
|
"--tools",
|
|
627
796
|
""
|
|
628
797
|
],
|
|
@@ -631,6 +800,7 @@ var Claude = class extends Harness {
|
|
|
631
800
|
"--output-format",
|
|
632
801
|
"json",
|
|
633
802
|
"{prompt}",
|
|
803
|
+
"--strict-mcp-config",
|
|
634
804
|
"--tools",
|
|
635
805
|
""
|
|
636
806
|
],
|
|
@@ -652,8 +822,14 @@ var Claude = class extends Harness {
|
|
|
652
822
|
],
|
|
653
823
|
readOnlyMinVersion: "2.1.175",
|
|
654
824
|
modelArgs: ["--model", "{model}"],
|
|
825
|
+
streamArgs: [
|
|
826
|
+
"--output-format",
|
|
827
|
+
"stream-json",
|
|
828
|
+
"--verbose",
|
|
829
|
+
"--include-partial-messages"
|
|
830
|
+
],
|
|
655
831
|
level: "official",
|
|
656
|
-
note: "Headless print mode; add --output-format json for structured output.
|
|
832
|
+
note: "Headless print mode; add --output-format json for structured output. Plain text runs stream events instead and fold them back, since text mode prints nothing until the answer is complete and a timeout would lose all of it; --include-partial-messages streams a single long answer too, checked on 2.1.280. --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
833
|
};
|
|
658
834
|
mcpConfigs = [{
|
|
659
835
|
path: "~/.claude.json",
|
|
@@ -670,6 +846,63 @@ var Claude = class extends Harness {
|
|
|
670
846
|
key: ["mcpServers"],
|
|
671
847
|
dialect: "standard"
|
|
672
848
|
}];
|
|
849
|
+
temp = [
|
|
850
|
+
{
|
|
851
|
+
path: "${TMPDIR}/claude-<uid>/",
|
|
852
|
+
scope: "data",
|
|
853
|
+
level: "official",
|
|
854
|
+
platforms: ["linux"],
|
|
855
|
+
note: "Session scratchpads sit under <dash-encoded-cwd>/<session-id>/scratchpad."
|
|
856
|
+
},
|
|
857
|
+
{
|
|
858
|
+
path: "/tmp/claude-<uid>/",
|
|
859
|
+
scope: "data",
|
|
860
|
+
level: "official",
|
|
861
|
+
platforms: ["darwin"]
|
|
862
|
+
},
|
|
863
|
+
{
|
|
864
|
+
path: "${TMPDIR}/claude/",
|
|
865
|
+
scope: "data",
|
|
866
|
+
level: "official",
|
|
867
|
+
platforms: ["win32"]
|
|
868
|
+
}
|
|
869
|
+
];
|
|
870
|
+
envOverrides = [
|
|
871
|
+
{
|
|
872
|
+
variable: "CLAUDE_CONFIG_DIR",
|
|
873
|
+
path: "~/.claude",
|
|
874
|
+
scope: "user",
|
|
875
|
+
level: "official",
|
|
876
|
+
relocates: [
|
|
877
|
+
"config",
|
|
878
|
+
"sessions",
|
|
879
|
+
"instructions",
|
|
880
|
+
"skills",
|
|
881
|
+
"commands",
|
|
882
|
+
"promptTemplates",
|
|
883
|
+
"hooks"
|
|
884
|
+
],
|
|
885
|
+
note: "Read from the shell or the env block of user or managed settings, not project settings."
|
|
886
|
+
},
|
|
887
|
+
{
|
|
888
|
+
variable: "CLAUDE_CODE_TMPDIR",
|
|
889
|
+
path: "${TMPDIR}",
|
|
890
|
+
scope: "data",
|
|
891
|
+
level: "official",
|
|
892
|
+
platforms: ["linux", "win32"],
|
|
893
|
+
relocates: ["temp"],
|
|
894
|
+
note: "Read from the shell or the env block of user or managed settings, not project settings."
|
|
895
|
+
},
|
|
896
|
+
{
|
|
897
|
+
variable: "CLAUDE_CODE_TMPDIR",
|
|
898
|
+
path: "/tmp",
|
|
899
|
+
scope: "data",
|
|
900
|
+
level: "official",
|
|
901
|
+
platforms: ["darwin"],
|
|
902
|
+
relocates: ["temp"],
|
|
903
|
+
note: "Read from the shell or the env block of user or managed settings, not project settings."
|
|
904
|
+
}
|
|
905
|
+
];
|
|
673
906
|
agentsFile = "~/.claude/CLAUDE.md";
|
|
674
907
|
detection = {
|
|
675
908
|
envVars: [
|
|
@@ -685,6 +918,9 @@ var Claude = class extends Harness {
|
|
|
685
918
|
"CLAUDE.md"
|
|
686
919
|
]
|
|
687
920
|
};
|
|
921
|
+
foldStreamOutput(stdout, complete) {
|
|
922
|
+
return foldClaudeStream(stdout, complete);
|
|
923
|
+
}
|
|
688
924
|
};
|
|
689
925
|
var Codex = class extends Harness {
|
|
690
926
|
id = "codex";
|
|
@@ -808,7 +1044,19 @@ var Codex = class extends Harness {
|
|
|
808
1044
|
platforms: ["linux", "darwin"]
|
|
809
1045
|
}
|
|
810
1046
|
];
|
|
811
|
-
commands = [
|
|
1047
|
+
commands = [{
|
|
1048
|
+
path: "~/.codex/prompts/",
|
|
1049
|
+
scope: "user",
|
|
1050
|
+
level: "official",
|
|
1051
|
+
note: "Deprecated Markdown prompts invoked as /prompts:name. Top-level files only; use skills for shared prompts."
|
|
1052
|
+
}];
|
|
1053
|
+
promptTemplates = this.commands;
|
|
1054
|
+
promptTemplateSyncTarget = {
|
|
1055
|
+
path: "~/.codex/prompts/",
|
|
1056
|
+
scope: "user",
|
|
1057
|
+
level: "official",
|
|
1058
|
+
format: "markdown"
|
|
1059
|
+
};
|
|
812
1060
|
hooks = [];
|
|
813
1061
|
invocation = {
|
|
814
1062
|
args: [
|
|
@@ -856,6 +1104,20 @@ var Codex = class extends Harness {
|
|
|
856
1104
|
key: ["mcp_servers"],
|
|
857
1105
|
dialect: "standard"
|
|
858
1106
|
}];
|
|
1107
|
+
envOverrides = [{
|
|
1108
|
+
variable: "CODEX_HOME",
|
|
1109
|
+
path: "~/.codex",
|
|
1110
|
+
scope: "user",
|
|
1111
|
+
level: "official",
|
|
1112
|
+
relocates: [
|
|
1113
|
+
"config",
|
|
1114
|
+
"sessions",
|
|
1115
|
+
"instructions",
|
|
1116
|
+
"skills",
|
|
1117
|
+
"commands",
|
|
1118
|
+
"promptTemplates"
|
|
1119
|
+
]
|
|
1120
|
+
}];
|
|
859
1121
|
agentsFile = "~/.codex/AGENTS.md";
|
|
860
1122
|
detection = {
|
|
861
1123
|
envVars: [],
|
|
@@ -1171,6 +1433,13 @@ var Gemini = class extends Harness {
|
|
|
1171
1433
|
level: "official",
|
|
1172
1434
|
note: "Global custom commands in TOML format."
|
|
1173
1435
|
}];
|
|
1436
|
+
promptTemplates = this.commands;
|
|
1437
|
+
promptTemplateSyncTarget = {
|
|
1438
|
+
path: "~/.gemini/commands/",
|
|
1439
|
+
scope: "user",
|
|
1440
|
+
level: "official",
|
|
1441
|
+
format: "gemini-toml"
|
|
1442
|
+
};
|
|
1174
1443
|
hooks = [];
|
|
1175
1444
|
invocation = {
|
|
1176
1445
|
args: ["-p", "{prompt}"],
|
|
@@ -1273,6 +1542,20 @@ var GitHubCopilot = class extends Harness {
|
|
|
1273
1542
|
]
|
|
1274
1543
|
};
|
|
1275
1544
|
};
|
|
1545
|
+
function parseGrokModels(stdout) {
|
|
1546
|
+
const lines = stripVTControlCharacters(stdout).split(/\r?\n/).map((line) => line.trim()).filter(Boolean);
|
|
1547
|
+
const start = lines.indexOf("Available models:");
|
|
1548
|
+
if (start === -1) throw new Error(`Unexpected Grok model-list output: ${JSON.stringify(stdout)}`);
|
|
1549
|
+
return lines.slice(start + 1).map((line) => {
|
|
1550
|
+
const match = /^([*-])\s+(\S+)(?:\s+\(default\))?$/.exec(line);
|
|
1551
|
+
if (!match?.[2]) throw new Error(`Unexpected Grok model-list row: ${JSON.stringify(line)}`);
|
|
1552
|
+
return {
|
|
1553
|
+
provider: "xai",
|
|
1554
|
+
id: match[2],
|
|
1555
|
+
...match[1] === "*" ? { default: true } : {}
|
|
1556
|
+
};
|
|
1557
|
+
});
|
|
1558
|
+
}
|
|
1276
1559
|
var Grok = class extends Harness {
|
|
1277
1560
|
id = "grok";
|
|
1278
1561
|
name = "xAI Grok CLI";
|
|
@@ -1405,6 +1688,11 @@ var Grok = class extends Harness {
|
|
|
1405
1688
|
level: "official",
|
|
1406
1689
|
note: "-p is short for --single; add --output-format json for structured output. --sandbox read-only is kernel-enforced (Landlock, Seatbelt) regardless of the inherited permission mode and still allows writes to ~/.grok and temp dirs; verified on Linux with 1.0.13 and 1.0.25."
|
|
1407
1690
|
};
|
|
1691
|
+
modelListing = {
|
|
1692
|
+
args: ["models"],
|
|
1693
|
+
level: "official",
|
|
1694
|
+
note: "Prints bare ids with the default marked; verified with 1.0.41."
|
|
1695
|
+
};
|
|
1408
1696
|
mcpConfigs = [{
|
|
1409
1697
|
path: "~/.grok/config.toml",
|
|
1410
1698
|
scope: "user",
|
|
@@ -1429,6 +1717,9 @@ var Grok = class extends Harness {
|
|
|
1429
1717
|
],
|
|
1430
1718
|
projectMarkers: [".grok"]
|
|
1431
1719
|
};
|
|
1720
|
+
parseModelListingOutput(stdout) {
|
|
1721
|
+
return parseGrokModels(stdout);
|
|
1722
|
+
}
|
|
1432
1723
|
};
|
|
1433
1724
|
function packageVersion(commandPath) {
|
|
1434
1725
|
try {
|
|
@@ -1488,6 +1779,12 @@ var MastraCode = class extends Harness {
|
|
|
1488
1779
|
level: "official",
|
|
1489
1780
|
note: "Global MCP server configurations."
|
|
1490
1781
|
},
|
|
1782
|
+
{
|
|
1783
|
+
path: ".mcp.json",
|
|
1784
|
+
scope: "project",
|
|
1785
|
+
level: "official",
|
|
1786
|
+
note: "Claude Code compatible project MCP servers, read below .mastracode/mcp.json."
|
|
1787
|
+
},
|
|
1491
1788
|
{
|
|
1492
1789
|
path: ".mastracode/hooks.json",
|
|
1493
1790
|
scope: "project",
|
|
@@ -1579,16 +1876,51 @@ var MastraCode = class extends Harness {
|
|
|
1579
1876
|
note: "Claude Code compatible."
|
|
1580
1877
|
}
|
|
1581
1878
|
];
|
|
1582
|
-
commands = [
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1879
|
+
commands = [
|
|
1880
|
+
{
|
|
1881
|
+
path: ".mastracode/commands/",
|
|
1882
|
+
scope: "project",
|
|
1883
|
+
level: "official",
|
|
1884
|
+
note: "Markdown prompt commands; subdirectories become colon-separated command names."
|
|
1885
|
+
},
|
|
1886
|
+
{
|
|
1887
|
+
path: "~/.mastracode/commands/",
|
|
1888
|
+
scope: "user",
|
|
1889
|
+
level: "official",
|
|
1890
|
+
note: "Global Markdown prompt commands."
|
|
1891
|
+
},
|
|
1892
|
+
{
|
|
1893
|
+
path: ".claude/commands/",
|
|
1894
|
+
scope: "project",
|
|
1895
|
+
level: "official",
|
|
1896
|
+
note: "Claude compatibility."
|
|
1897
|
+
},
|
|
1898
|
+
{
|
|
1899
|
+
path: "~/.claude/commands/",
|
|
1900
|
+
scope: "user",
|
|
1901
|
+
level: "official",
|
|
1902
|
+
note: "Claude compatibility."
|
|
1903
|
+
},
|
|
1904
|
+
{
|
|
1905
|
+
path: ".opencode/command/",
|
|
1906
|
+
scope: "project",
|
|
1907
|
+
level: "official",
|
|
1908
|
+
note: "Mastra's OpenCode compatibility loader uses the singular command directory."
|
|
1909
|
+
},
|
|
1910
|
+
{
|
|
1911
|
+
path: "~/.opencode/command/",
|
|
1912
|
+
scope: "user",
|
|
1913
|
+
level: "official",
|
|
1914
|
+
note: "Mastra's OpenCode compatibility loader uses the singular command directory."
|
|
1915
|
+
}
|
|
1916
|
+
];
|
|
1917
|
+
promptTemplates = this.commands;
|
|
1918
|
+
promptTemplateSyncTarget = {
|
|
1587
1919
|
path: "~/.mastracode/commands/",
|
|
1588
1920
|
scope: "user",
|
|
1589
1921
|
level: "official",
|
|
1590
|
-
|
|
1591
|
-
}
|
|
1922
|
+
format: "markdown"
|
|
1923
|
+
};
|
|
1592
1924
|
hooks = [{
|
|
1593
1925
|
path: ".mastracode/hooks.json",
|
|
1594
1926
|
scope: "project",
|
|
@@ -1600,6 +1932,35 @@ var MastraCode = class extends Harness {
|
|
|
1600
1932
|
note: "Global hooks."
|
|
1601
1933
|
}];
|
|
1602
1934
|
invocation = null;
|
|
1935
|
+
mcpConfigs = [
|
|
1936
|
+
{
|
|
1937
|
+
path: "~/.mastracode/mcp.json",
|
|
1938
|
+
scope: "user",
|
|
1939
|
+
level: "official",
|
|
1940
|
+
format: "json",
|
|
1941
|
+
key: ["mcpServers"],
|
|
1942
|
+
dialect: "mastracode",
|
|
1943
|
+
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."
|
|
1944
|
+
},
|
|
1945
|
+
{
|
|
1946
|
+
path: ".mastracode/mcp.json",
|
|
1947
|
+
scope: "project",
|
|
1948
|
+
level: "official",
|
|
1949
|
+
format: "json",
|
|
1950
|
+
key: ["mcpServers"],
|
|
1951
|
+
dialect: "mastracode",
|
|
1952
|
+
note: "Highest priority, overrides the other files entry by entry."
|
|
1953
|
+
},
|
|
1954
|
+
{
|
|
1955
|
+
path: ".mcp.json",
|
|
1956
|
+
scope: "project",
|
|
1957
|
+
level: "official",
|
|
1958
|
+
format: "json",
|
|
1959
|
+
key: ["mcpServers"],
|
|
1960
|
+
dialect: "mastracode",
|
|
1961
|
+
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."
|
|
1962
|
+
}
|
|
1963
|
+
];
|
|
1603
1964
|
detection = {
|
|
1604
1965
|
envVars: [],
|
|
1605
1966
|
projectMarkers: [".mastracode"]
|
|
@@ -1622,7 +1983,7 @@ var Omp = class extends Harness {
|
|
|
1622
1983
|
path: "~/.omp/agent/config.yml",
|
|
1623
1984
|
scope: "user",
|
|
1624
1985
|
level: "official",
|
|
1625
|
-
note: "
|
|
1986
|
+
note: "Profiles use ~/.omp/profiles/<name>/agent."
|
|
1626
1987
|
},
|
|
1627
1988
|
{
|
|
1628
1989
|
path: "~/.omp/agent/settings.json",
|
|
@@ -1747,13 +2108,35 @@ var Omp = class extends Harness {
|
|
|
1747
2108
|
commands = [{
|
|
1748
2109
|
path: ".omp/commands/",
|
|
1749
2110
|
scope: "project",
|
|
1750
|
-
level: "official"
|
|
2111
|
+
level: "official",
|
|
2112
|
+
note: "Markdown prompt commands and TypeScript commands; only Markdown files are templates."
|
|
1751
2113
|
}, {
|
|
1752
2114
|
path: "~/.omp/agent/commands/",
|
|
1753
2115
|
scope: "user",
|
|
1754
2116
|
level: "official",
|
|
1755
|
-
note: "Global
|
|
2117
|
+
note: "Global Markdown prompt commands and TypeScript commands; only Markdown files are templates."
|
|
1756
2118
|
}];
|
|
2119
|
+
promptTemplates = [
|
|
2120
|
+
...this.commands,
|
|
2121
|
+
{
|
|
2122
|
+
path: ".omp/prompts/",
|
|
2123
|
+
scope: "project",
|
|
2124
|
+
level: "official",
|
|
2125
|
+
note: "Markdown prompt templates invoked as /name; subdirectories are scanned."
|
|
2126
|
+
},
|
|
2127
|
+
{
|
|
2128
|
+
path: "~/.omp/agent/prompts/",
|
|
2129
|
+
scope: "user",
|
|
2130
|
+
level: "official",
|
|
2131
|
+
note: "Global Markdown prompt templates, separate from executable commands."
|
|
2132
|
+
}
|
|
2133
|
+
];
|
|
2134
|
+
promptTemplateSyncTarget = {
|
|
2135
|
+
path: "~/.omp/agent/prompts/",
|
|
2136
|
+
scope: "user",
|
|
2137
|
+
level: "official",
|
|
2138
|
+
format: "markdown"
|
|
2139
|
+
};
|
|
1757
2140
|
hooks = [];
|
|
1758
2141
|
invocation = {
|
|
1759
2142
|
args: ["-p", "{prompt}"],
|
|
@@ -1782,6 +2165,21 @@ var Omp = class extends Harness {
|
|
|
1782
2165
|
key: ["mcpServers"],
|
|
1783
2166
|
dialect: "standard"
|
|
1784
2167
|
}];
|
|
2168
|
+
envOverrides = [{
|
|
2169
|
+
variable: "PI_CODING_AGENT_DIR",
|
|
2170
|
+
path: "~/.omp/agent",
|
|
2171
|
+
scope: "user",
|
|
2172
|
+
level: "official",
|
|
2173
|
+
relocates: [
|
|
2174
|
+
"config",
|
|
2175
|
+
"sessions",
|
|
2176
|
+
"instructions",
|
|
2177
|
+
"skills",
|
|
2178
|
+
"commands",
|
|
2179
|
+
"promptTemplates"
|
|
2180
|
+
],
|
|
2181
|
+
note: "Ignored under a named profile (OMP_PROFILE). On Linux, after omp config init-xdg, data, state and cache live under $XDG_*_HOME/omp instead."
|
|
2182
|
+
}];
|
|
1785
2183
|
agentsFile = "~/.omp/agent/AGENTS.md";
|
|
1786
2184
|
detection = {
|
|
1787
2185
|
envVars: ["OMP_PROFILE", "PI_CODING_AGENT_DIR"],
|
|
@@ -1896,7 +2294,24 @@ var OpenCode = class extends Harness {
|
|
|
1896
2294
|
level: "official"
|
|
1897
2295
|
}
|
|
1898
2296
|
];
|
|
1899
|
-
commands = [
|
|
2297
|
+
commands = [{
|
|
2298
|
+
path: ".opencode/commands/",
|
|
2299
|
+
scope: "project",
|
|
2300
|
+
level: "official",
|
|
2301
|
+
note: "Markdown prompt commands; also configurable through command entries in opencode.json."
|
|
2302
|
+
}, {
|
|
2303
|
+
path: "~/.config/opencode/commands/",
|
|
2304
|
+
scope: "user",
|
|
2305
|
+
level: "official",
|
|
2306
|
+
note: "Global Markdown prompt commands."
|
|
2307
|
+
}];
|
|
2308
|
+
promptTemplates = this.commands;
|
|
2309
|
+
promptTemplateSyncTarget = {
|
|
2310
|
+
path: "~/.config/opencode/commands/",
|
|
2311
|
+
scope: "user",
|
|
2312
|
+
level: "official",
|
|
2313
|
+
format: "markdown"
|
|
2314
|
+
};
|
|
1900
2315
|
hooks = [];
|
|
1901
2316
|
invocation = {
|
|
1902
2317
|
args: ["run", "{prompt}"],
|
|
@@ -1989,8 +2404,7 @@ var Pi = class extends Harness {
|
|
|
1989
2404
|
config = [{
|
|
1990
2405
|
path: "~/.pi/agent/settings.json",
|
|
1991
2406
|
scope: "user",
|
|
1992
|
-
level: "official"
|
|
1993
|
-
note: "Config dir overridable with PI_CODING_AGENT_DIR."
|
|
2407
|
+
level: "official"
|
|
1994
2408
|
}, {
|
|
1995
2409
|
path: ".pi/settings.json",
|
|
1996
2410
|
scope: "project",
|
|
@@ -2000,7 +2414,7 @@ var Pi = class extends Harness {
|
|
|
2000
2414
|
path: "~/.pi/agent/sessions/<dash-encoded-cwd>/<timestamp>_<session-id>.jsonl",
|
|
2001
2415
|
scope: "data",
|
|
2002
2416
|
level: "official",
|
|
2003
|
-
note: "Per-project session transcripts in JSONL format; session ids are UUIDv7. Overridable with
|
|
2417
|
+
note: "Per-project session transcripts in JSONL format; session ids are UUIDv7. Overridable with --session-dir."
|
|
2004
2418
|
}];
|
|
2005
2419
|
persistence = [{
|
|
2006
2420
|
format: "JSON",
|
|
@@ -2058,6 +2472,13 @@ var Pi = class extends Harness {
|
|
|
2058
2472
|
level: "official",
|
|
2059
2473
|
note: "Global user-level prompt templates."
|
|
2060
2474
|
}];
|
|
2475
|
+
promptTemplates = this.commands;
|
|
2476
|
+
promptTemplateSyncTarget = {
|
|
2477
|
+
path: "~/.pi/agent/prompts/",
|
|
2478
|
+
scope: "user",
|
|
2479
|
+
level: "official",
|
|
2480
|
+
format: "markdown"
|
|
2481
|
+
};
|
|
2061
2482
|
hooks = [];
|
|
2062
2483
|
invocation = {
|
|
2063
2484
|
args: ["-p", "{prompt}"],
|
|
@@ -2103,6 +2524,28 @@ var Pi = class extends Harness {
|
|
|
2103
2524
|
level: "official",
|
|
2104
2525
|
note: "Lists models with configured provider authentication; accepts an optional fuzzy search."
|
|
2105
2526
|
};
|
|
2527
|
+
envOverrides = [{
|
|
2528
|
+
variable: "PI_CODING_AGENT_DIR",
|
|
2529
|
+
path: "~/.pi/agent",
|
|
2530
|
+
scope: "user",
|
|
2531
|
+
level: "official",
|
|
2532
|
+
relocates: [
|
|
2533
|
+
"config",
|
|
2534
|
+
"sessions",
|
|
2535
|
+
"instructions",
|
|
2536
|
+
"skills",
|
|
2537
|
+
"commands",
|
|
2538
|
+
"promptTemplates"
|
|
2539
|
+
],
|
|
2540
|
+
note: "A leading ~ in the value is expanded."
|
|
2541
|
+
}, {
|
|
2542
|
+
variable: "PI_CODING_AGENT_SESSION_DIR",
|
|
2543
|
+
path: "~/.pi/agent/sessions",
|
|
2544
|
+
scope: "data",
|
|
2545
|
+
level: "official",
|
|
2546
|
+
relocates: ["sessions"],
|
|
2547
|
+
note: "Takes precedence over PI_CODING_AGENT_DIR for sessions; --session-dir beats both."
|
|
2548
|
+
}];
|
|
2106
2549
|
agentsFile = "~/.pi/agent/AGENTS.md";
|
|
2107
2550
|
detection = {
|
|
2108
2551
|
envVars: [
|
|
@@ -2228,6 +2671,13 @@ var PrimeAgent = class extends Harness {
|
|
|
2228
2671
|
level: "official",
|
|
2229
2672
|
note: "Global user-level prompt templates."
|
|
2230
2673
|
}];
|
|
2674
|
+
promptTemplates = this.commands;
|
|
2675
|
+
promptTemplateSyncTarget = {
|
|
2676
|
+
path: "~/.prime/agent/prompts/",
|
|
2677
|
+
scope: "user",
|
|
2678
|
+
level: "official",
|
|
2679
|
+
format: "markdown"
|
|
2680
|
+
};
|
|
2231
2681
|
hooks = [];
|
|
2232
2682
|
invocation = {
|
|
2233
2683
|
args: ["-p", "{prompt}"],
|
|
@@ -2457,8 +2907,21 @@ function normalizePrime(name, raw) {
|
|
|
2457
2907
|
env: primeEnv(raw.env)
|
|
2458
2908
|
});
|
|
2459
2909
|
}
|
|
2910
|
+
function normalizeMastracode(name, raw) {
|
|
2911
|
+
const url = stringField(raw, "url");
|
|
2912
|
+
return compact({
|
|
2913
|
+
name,
|
|
2914
|
+
transport: url ? "http" : "stdio",
|
|
2915
|
+
command: stringField(raw, "command"),
|
|
2916
|
+
args: asStringArray(raw.args),
|
|
2917
|
+
env: asStringRecord(raw.env),
|
|
2918
|
+
url,
|
|
2919
|
+
headers: asStringRecord(raw.headers)
|
|
2920
|
+
});
|
|
2921
|
+
}
|
|
2460
2922
|
function normalizeEntry(name, raw, dialect) {
|
|
2461
2923
|
if (dialect === "antigravity") return normalizeAntigravity(name, raw);
|
|
2924
|
+
if (dialect === "mastracode") return normalizeMastracode(name, raw);
|
|
2462
2925
|
if (dialect === "opencode") return normalizeOpenCode(name, raw);
|
|
2463
2926
|
if (dialect === "prime") return normalizePrime(name, raw);
|
|
2464
2927
|
return normalizeStandard(name, raw);
|
|
@@ -2485,6 +2948,17 @@ function denormalizeOpenCode(server) {
|
|
|
2485
2948
|
enabled: server.enabled
|
|
2486
2949
|
});
|
|
2487
2950
|
}
|
|
2951
|
+
function denormalizeMastracode(server) {
|
|
2952
|
+
if (server.url) return compact({
|
|
2953
|
+
url: server.url,
|
|
2954
|
+
headers: server.headers
|
|
2955
|
+
});
|
|
2956
|
+
return compact({
|
|
2957
|
+
command: server.command,
|
|
2958
|
+
args: server.args?.length ? server.args : void 0,
|
|
2959
|
+
env: server.env
|
|
2960
|
+
});
|
|
2961
|
+
}
|
|
2488
2962
|
function denormalizeStandard(server) {
|
|
2489
2963
|
return compact({
|
|
2490
2964
|
type: server.transport,
|
|
@@ -2530,8 +3004,18 @@ function resolveEnvReferences(server, dialect) {
|
|
|
2530
3004
|
env
|
|
2531
3005
|
};
|
|
2532
3006
|
}
|
|
3007
|
+
function shapeForDialect(server, dialect) {
|
|
3008
|
+
const shaped = resolveEnvReferences(server, dialect);
|
|
3009
|
+
if (dialect !== "mastracode") return shaped;
|
|
3010
|
+
return compact({
|
|
3011
|
+
...shaped,
|
|
3012
|
+
transport: shaped.url ? "http" : "stdio",
|
|
3013
|
+
enabled: void 0
|
|
3014
|
+
});
|
|
3015
|
+
}
|
|
2533
3016
|
function denormalizeEntry(server, dialect) {
|
|
2534
3017
|
if (dialect === "antigravity") return denormalizeAntigravity(server);
|
|
3018
|
+
if (dialect === "mastracode") return denormalizeMastracode(server);
|
|
2535
3019
|
if (dialect === "opencode") return denormalizeOpenCode(server);
|
|
2536
3020
|
if (dialect === "prime") return denormalizePrime(server);
|
|
2537
3021
|
return denormalizeStandard(server);
|
|
@@ -2667,7 +3151,7 @@ function removeTomlServer(path, key, name) {
|
|
|
2667
3151
|
}
|
|
2668
3152
|
function addMcpServer(harness, server, scope = "user", options = {}) {
|
|
2669
3153
|
const { entry, path } = writableConfig(harness, scope, options);
|
|
2670
|
-
const resolved =
|
|
3154
|
+
const resolved = shapeForDialect(server, entry.dialect);
|
|
2671
3155
|
if (entry.format === "toml") return {
|
|
2672
3156
|
path,
|
|
2673
3157
|
replaced: addTomlServer(path, entry.key, resolved)
|
|
@@ -2887,7 +3371,7 @@ function syncIncludedHarness(harness, master, existing, masterNames, initialPath
|
|
|
2887
3371
|
let path = initialPath;
|
|
2888
3372
|
const { dialect } = writableConfig(harness, "user", options).entry;
|
|
2889
3373
|
for (const server of master.servers) try {
|
|
2890
|
-
const resolved =
|
|
3374
|
+
const resolved = shapeForDialect(server, dialect);
|
|
2891
3375
|
if (existing.get(server.name) === canonical(resolved)) {
|
|
2892
3376
|
results.push({
|
|
2893
3377
|
name: server.name,
|
|
@@ -3035,6 +3519,23 @@ function relink(path, source) {
|
|
|
3035
3519
|
symlinkSync(source, temp);
|
|
3036
3520
|
renameSync(temp, path);
|
|
3037
3521
|
}
|
|
3522
|
+
function moveFile(from, to) {
|
|
3523
|
+
try {
|
|
3524
|
+
renameSync(from, to);
|
|
3525
|
+
} catch (error) {
|
|
3526
|
+
if (error.code !== "EXDEV") throw error;
|
|
3527
|
+
if (lstatSync(from).isDirectory()) {
|
|
3528
|
+
cpSync(from, to, {
|
|
3529
|
+
recursive: true,
|
|
3530
|
+
verbatimSymlinks: true
|
|
3531
|
+
});
|
|
3532
|
+
rmSync(from, { recursive: true });
|
|
3533
|
+
return;
|
|
3534
|
+
}
|
|
3535
|
+
copyFileSync(from, to);
|
|
3536
|
+
unlinkSync(from);
|
|
3537
|
+
}
|
|
3538
|
+
}
|
|
3038
3539
|
const CHECK_ACTION = {
|
|
3039
3540
|
missing: "linked",
|
|
3040
3541
|
"correct-link": "unchanged",
|
|
@@ -3047,7 +3548,7 @@ function applyFileTarget(id, path, source, state, options) {
|
|
|
3047
3548
|
const backupDir = join(agntnConfigDir(options), "diverged");
|
|
3048
3549
|
mkdirSync(backupDir, { recursive: true });
|
|
3049
3550
|
const backup = join(backupDir, `${id}-${basename(path)}-${Date.now()}.md`);
|
|
3050
|
-
|
|
3551
|
+
moveFile(path, backup);
|
|
3051
3552
|
relink(path, source);
|
|
3052
3553
|
return {
|
|
3053
3554
|
path,
|
|
@@ -3082,7 +3583,7 @@ function syncFileTarget(id, path, source, check, options) {
|
|
|
3082
3583
|
};
|
|
3083
3584
|
return applyFileTarget(id, path, source, state, options);
|
|
3084
3585
|
}
|
|
3085
|
-
function syncTarget(harness, config, check, options) {
|
|
3586
|
+
function syncTarget$1(harness, config, check, options) {
|
|
3086
3587
|
if (config.excludes.includes(harness.id)) return {
|
|
3087
3588
|
id: harness.id,
|
|
3088
3589
|
action: "skipped",
|
|
@@ -3133,9 +3634,321 @@ function syncAgentsFiles(harnesses, check = false, options = {}) {
|
|
|
3133
3634
|
return {
|
|
3134
3635
|
source: config.source,
|
|
3135
3636
|
check,
|
|
3136
|
-
targets: harnesses.map((harness) => syncTarget(harness, config, check, options))
|
|
3637
|
+
targets: harnesses.map((harness) => syncTarget$1(harness, config, check, options))
|
|
3638
|
+
};
|
|
3639
|
+
}
|
|
3640
|
+
const GENERATED_MARKER = "# Generated by @agntn/harnesses from ";
|
|
3641
|
+
const PORTABLE_PROMPT_NAME = /^[A-Za-z0-9][A-Za-z0-9_-]*$/u;
|
|
3642
|
+
const GEMINI_ACTION = {
|
|
3643
|
+
missing: "generated",
|
|
3644
|
+
"owned-file": "replaced",
|
|
3645
|
+
"unmanaged-file": "adopted",
|
|
3646
|
+
unsupported: "skipped"
|
|
3647
|
+
};
|
|
3648
|
+
const GEMINI_BACKUP = {
|
|
3649
|
+
missing: false,
|
|
3650
|
+
"owned-file": true,
|
|
3651
|
+
"unmanaged-file": true,
|
|
3652
|
+
unsupported: false
|
|
3653
|
+
};
|
|
3654
|
+
function readTemplates(sourceDir, check) {
|
|
3655
|
+
let entries;
|
|
3656
|
+
try {
|
|
3657
|
+
entries = readdirSync(sourceDir, { withFileTypes: true });
|
|
3658
|
+
} catch (error) {
|
|
3659
|
+
if (error.code !== "ENOENT") throw error;
|
|
3660
|
+
if (!check) mkdirSync(sourceDir, { recursive: true });
|
|
3661
|
+
return [];
|
|
3662
|
+
}
|
|
3663
|
+
const templates = entries.filter((entry) => (entry.isFile() || entry.isSymbolicLink()) && extname(entry.name).toLowerCase() === ".md").map((entry) => {
|
|
3664
|
+
const name = basename(entry.name, extname(entry.name));
|
|
3665
|
+
if (!PORTABLE_PROMPT_NAME.test(name)) throw new TypeError(`Invalid prompt template filename: ${entry.name}`);
|
|
3666
|
+
const path = join(sourceDir, entry.name);
|
|
3667
|
+
return {
|
|
3668
|
+
name,
|
|
3669
|
+
path,
|
|
3670
|
+
content: readFileSync(path, "utf8")
|
|
3671
|
+
};
|
|
3672
|
+
}).sort((left, right) => left.name.localeCompare(right.name));
|
|
3673
|
+
const names = templates.map((template) => template.name.toLowerCase());
|
|
3674
|
+
if (new Set(names).size !== names.length) throw new Error(`Duplicate prompt template names in ${sourceDir}`);
|
|
3675
|
+
return templates;
|
|
3676
|
+
}
|
|
3677
|
+
function generatedSource(content) {
|
|
3678
|
+
const firstLine = content.split("\n", 1)[0] ?? "";
|
|
3679
|
+
if (!firstLine.startsWith(GENERATED_MARKER)) return null;
|
|
3680
|
+
try {
|
|
3681
|
+
const source = JSON.parse(firstLine.slice(37));
|
|
3682
|
+
return typeof source === "string" ? source : null;
|
|
3683
|
+
} catch {
|
|
3684
|
+
return null;
|
|
3685
|
+
}
|
|
3686
|
+
}
|
|
3687
|
+
function linkTarget(path) {
|
|
3688
|
+
const target = readlinkSync(path);
|
|
3689
|
+
return isAbsolute(target) ? target : resolve(dirname(path), target);
|
|
3690
|
+
}
|
|
3691
|
+
function inspectDestination(path) {
|
|
3692
|
+
let stats;
|
|
3693
|
+
try {
|
|
3694
|
+
stats = lstatSync(path);
|
|
3695
|
+
} catch (error) {
|
|
3696
|
+
if (error.code === "ENOENT") return { kind: "missing" };
|
|
3697
|
+
throw error;
|
|
3698
|
+
}
|
|
3699
|
+
if (stats.isSymbolicLink()) return { kind: "unmanaged-file" };
|
|
3700
|
+
if (!stats.isFile()) return { kind: "unsupported" };
|
|
3701
|
+
const content = readFileSync(path, "utf8");
|
|
3702
|
+
if (generatedSource(content) !== null) return {
|
|
3703
|
+
kind: "owned-file",
|
|
3704
|
+
content
|
|
3705
|
+
};
|
|
3706
|
+
return { kind: "unmanaged-file" };
|
|
3707
|
+
}
|
|
3708
|
+
function tempPath(path, kind) {
|
|
3709
|
+
return join(dirname(path), `.${basename(path)}-${randomUUID()}.${kind}.tmp`);
|
|
3710
|
+
}
|
|
3711
|
+
function linkDirectory(path, source) {
|
|
3712
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
3713
|
+
const temp = tempPath(path, "prompt-link");
|
|
3714
|
+
symlinkSync(source, temp, "junction");
|
|
3715
|
+
renameSync(temp, path);
|
|
3716
|
+
}
|
|
3717
|
+
function writeTemplate(path, content) {
|
|
3718
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
3719
|
+
const temp = tempPath(path, "prompt-write");
|
|
3720
|
+
writeFileSync(temp, content);
|
|
3721
|
+
renameSync(temp, path);
|
|
3722
|
+
}
|
|
3723
|
+
function backupDestination(id, path, options) {
|
|
3724
|
+
const backupDir = join(agntnDataDir(options), "diverged", "prompts", id);
|
|
3725
|
+
mkdirSync(backupDir, { recursive: true });
|
|
3726
|
+
const backup = join(backupDir, `${basename(path)}-${Date.now()}-${randomUUID()}`);
|
|
3727
|
+
if (lstatSync(path).isSymbolicLink()) {
|
|
3728
|
+
symlinkSync(linkTarget(path), backup);
|
|
3729
|
+
unlinkSync(path);
|
|
3730
|
+
return backup;
|
|
3731
|
+
}
|
|
3732
|
+
moveFile(path, backup);
|
|
3733
|
+
return backup;
|
|
3734
|
+
}
|
|
3735
|
+
function checkDetail() {
|
|
3736
|
+
return "check mode: not applied";
|
|
3737
|
+
}
|
|
3738
|
+
function isInsideDirectory(path, directory) {
|
|
3739
|
+
const remainder = relative(directory, path);
|
|
3740
|
+
return remainder !== "" && !remainder.startsWith("..") && !isAbsolute(remainder);
|
|
3741
|
+
}
|
|
3742
|
+
function isLegacyTemplateLink(path, sourceDir) {
|
|
3743
|
+
return lstatSync(path).isSymbolicLink() && isInsideDirectory(linkTarget(path), sourceDir);
|
|
3744
|
+
}
|
|
3745
|
+
const DIRECTORY_ACTION = {
|
|
3746
|
+
missing: "linked",
|
|
3747
|
+
"correct-link": "unchanged",
|
|
3748
|
+
"wrong-link": "relinked",
|
|
3749
|
+
"legacy-links": "relinked",
|
|
3750
|
+
"local-files": "adopted",
|
|
3751
|
+
"not-directory": "skipped"
|
|
3752
|
+
};
|
|
3753
|
+
function inspectDirectory(targetDir, sourceDir) {
|
|
3754
|
+
let stats;
|
|
3755
|
+
try {
|
|
3756
|
+
stats = lstatSync(targetDir);
|
|
3757
|
+
} catch (error) {
|
|
3758
|
+
if (error.code === "ENOENT") return { kind: "missing" };
|
|
3759
|
+
throw error;
|
|
3760
|
+
}
|
|
3761
|
+
if (stats.isSymbolicLink()) return { kind: linkTarget(targetDir) === sourceDir ? "correct-link" : "wrong-link" };
|
|
3762
|
+
if (!stats.isDirectory()) return { kind: "not-directory" };
|
|
3763
|
+
const entries = readdirSync(targetDir).map((entry) => join(targetDir, entry));
|
|
3764
|
+
return entries.every((entry) => isLegacyTemplateLink(entry, sourceDir)) ? {
|
|
3765
|
+
kind: "legacy-links",
|
|
3766
|
+
entries
|
|
3767
|
+
} : { kind: "local-files" };
|
|
3768
|
+
}
|
|
3769
|
+
function clearDirectory(id, targetDir, state, options) {
|
|
3770
|
+
if (state.kind === "wrong-link" || state.kind === "local-files") return backupDestination(id, targetDir, options);
|
|
3771
|
+
if (state.kind === "legacy-links") {
|
|
3772
|
+
for (const entry of state.entries) unlinkSync(entry);
|
|
3773
|
+
rmdirSync(targetDir);
|
|
3774
|
+
}
|
|
3775
|
+
}
|
|
3776
|
+
function syncMarkdownDirectory(id, targetDir, sourceDir, check, options) {
|
|
3777
|
+
const state = inspectDirectory(targetDir, sourceDir);
|
|
3778
|
+
const action = DIRECTORY_ACTION[state.kind];
|
|
3779
|
+
if (state.kind === "correct-link") return { action };
|
|
3780
|
+
if (state.kind === "not-directory") return {
|
|
3781
|
+
action,
|
|
3782
|
+
detail: "destination exists and is not a directory or symlink"
|
|
3783
|
+
};
|
|
3784
|
+
if (check) return {
|
|
3785
|
+
action,
|
|
3786
|
+
detail: checkDetail()
|
|
3787
|
+
};
|
|
3788
|
+
const detail = clearDirectory(id, targetDir, state, options);
|
|
3789
|
+
linkDirectory(targetDir, sourceDir);
|
|
3790
|
+
return {
|
|
3791
|
+
action,
|
|
3792
|
+
detail
|
|
3793
|
+
};
|
|
3794
|
+
}
|
|
3795
|
+
function parseYaml(raw) {
|
|
3796
|
+
return createRequire(import.meta.url)("yaml").parse(raw);
|
|
3797
|
+
}
|
|
3798
|
+
function frontmatterDescription(raw, path) {
|
|
3799
|
+
const frontmatter = parseYaml(raw);
|
|
3800
|
+
if (frontmatter === null) return void 0;
|
|
3801
|
+
if (typeof frontmatter !== "object" || Array.isArray(frontmatter)) throw new TypeError(`Prompt template frontmatter is not an object: ${path}`);
|
|
3802
|
+
const description = frontmatter.description;
|
|
3803
|
+
if (description !== void 0 && typeof description !== "string") throw new TypeError(`Prompt template description is not a string: ${path}`);
|
|
3804
|
+
return description;
|
|
3805
|
+
}
|
|
3806
|
+
function parseMarkdownTemplate(template) {
|
|
3807
|
+
const match = /^---\r?\n([\s\S]*?)\r?\n---(?:\r?\n|$)/.exec(template.content);
|
|
3808
|
+
if (match === null) return { prompt: template.content.replaceAll("$ARGUMENTS", "{{args}}") };
|
|
3809
|
+
const description = frontmatterDescription(match[1] ?? "", template.path);
|
|
3810
|
+
return {
|
|
3811
|
+
...description === void 0 ? {} : { description },
|
|
3812
|
+
prompt: template.content.slice(match[0].length).replaceAll("$ARGUMENTS", "{{args}}")
|
|
3813
|
+
};
|
|
3814
|
+
}
|
|
3815
|
+
function geminiContent(template) {
|
|
3816
|
+
const parsed = parseMarkdownTemplate(template);
|
|
3817
|
+
return [
|
|
3818
|
+
`${GENERATED_MARKER}${JSON.stringify(template.path)}`,
|
|
3819
|
+
...parsed.description === void 0 ? [] : [`description = ${JSON.stringify(parsed.description)}`],
|
|
3820
|
+
`prompt = ${JSON.stringify(parsed.prompt)}`,
|
|
3821
|
+
""
|
|
3822
|
+
].join("\n");
|
|
3823
|
+
}
|
|
3824
|
+
function syncGeminiTemplate(id, targetDir, template, check, options) {
|
|
3825
|
+
const path = join(targetDir, `${template.name}.toml`);
|
|
3826
|
+
const content = template.gemini ?? geminiContent(template);
|
|
3827
|
+
const state = inspectDestination(path);
|
|
3828
|
+
if (state.kind === "owned-file" && state.content === content) return {
|
|
3829
|
+
name: template.name,
|
|
3830
|
+
source: template.path,
|
|
3831
|
+
path,
|
|
3832
|
+
action: "unchanged"
|
|
3833
|
+
};
|
|
3834
|
+
if (state.kind === "unsupported") return {
|
|
3835
|
+
name: template.name,
|
|
3836
|
+
source: template.path,
|
|
3837
|
+
path,
|
|
3838
|
+
action: "skipped",
|
|
3839
|
+
detail: "destination exists and is not a file or symlink"
|
|
3840
|
+
};
|
|
3841
|
+
const action = GEMINI_ACTION[state.kind];
|
|
3842
|
+
if (check) return {
|
|
3843
|
+
name: template.name,
|
|
3844
|
+
source: template.path,
|
|
3845
|
+
path,
|
|
3846
|
+
action,
|
|
3847
|
+
detail: checkDetail()
|
|
3848
|
+
};
|
|
3849
|
+
let detail;
|
|
3850
|
+
if (GEMINI_BACKUP[state.kind]) detail = backupDestination(id, path, options);
|
|
3851
|
+
writeTemplate(path, content);
|
|
3852
|
+
return {
|
|
3853
|
+
name: template.name,
|
|
3854
|
+
source: template.path,
|
|
3855
|
+
path,
|
|
3856
|
+
action,
|
|
3857
|
+
detail
|
|
3858
|
+
};
|
|
3859
|
+
}
|
|
3860
|
+
function staleGeminiSource(path, sourceDir) {
|
|
3861
|
+
let content;
|
|
3862
|
+
try {
|
|
3863
|
+
content = readFileSync(path, "utf8");
|
|
3864
|
+
} catch {
|
|
3865
|
+
return null;
|
|
3866
|
+
}
|
|
3867
|
+
const source = generatedSource(content);
|
|
3868
|
+
if (source === null) return null;
|
|
3869
|
+
return isInsideDirectory(source, sourceDir) ? source : null;
|
|
3870
|
+
}
|
|
3871
|
+
function pruneGeminiTarget(id, targetDir, sourceDir, activeNames, check, options) {
|
|
3872
|
+
let entries;
|
|
3873
|
+
try {
|
|
3874
|
+
entries = readdirSync(targetDir);
|
|
3875
|
+
} catch (error) {
|
|
3876
|
+
if (error.code === "ENOENT") return [];
|
|
3877
|
+
throw error;
|
|
3878
|
+
}
|
|
3879
|
+
return entries.filter((entry) => !activeNames.has(entry)).map((entry) => {
|
|
3880
|
+
const path = join(targetDir, entry);
|
|
3881
|
+
const name = basename(entry, extname(entry));
|
|
3882
|
+
const source = staleGeminiSource(path, sourceDir);
|
|
3883
|
+
if (check) return {
|
|
3884
|
+
name,
|
|
3885
|
+
...source === null ? {} : { source },
|
|
3886
|
+
path,
|
|
3887
|
+
action: "removed",
|
|
3888
|
+
detail: checkDetail()
|
|
3889
|
+
};
|
|
3890
|
+
if (source !== null) {
|
|
3891
|
+
unlinkSync(path);
|
|
3892
|
+
return {
|
|
3893
|
+
name,
|
|
3894
|
+
source,
|
|
3895
|
+
path,
|
|
3896
|
+
action: "removed"
|
|
3897
|
+
};
|
|
3898
|
+
}
|
|
3899
|
+
return {
|
|
3900
|
+
name,
|
|
3901
|
+
path,
|
|
3902
|
+
action: "removed",
|
|
3903
|
+
detail: backupDestination(id, path, options)
|
|
3904
|
+
};
|
|
3905
|
+
});
|
|
3906
|
+
}
|
|
3907
|
+
function syncTarget(harness, templates, sourceDir, check, options) {
|
|
3908
|
+
const target = harness.promptTemplateSyncTarget;
|
|
3909
|
+
if (target === null) return {
|
|
3910
|
+
id: harness.id,
|
|
3911
|
+
detail: "no stable user-scope prompt template destination",
|
|
3912
|
+
templates: []
|
|
3913
|
+
};
|
|
3914
|
+
const resolved = harness.resolveCandidates([target], options)[0];
|
|
3915
|
+
if (resolved === void 0) return {
|
|
3916
|
+
id: harness.id,
|
|
3917
|
+
detail: "prompt template destination is unavailable on this platform",
|
|
3918
|
+
templates: []
|
|
3919
|
+
};
|
|
3920
|
+
const targetDir = resolve(resolved.path);
|
|
3921
|
+
if (resolved.format === "markdown") return {
|
|
3922
|
+
id: harness.id,
|
|
3923
|
+
path: targetDir,
|
|
3924
|
+
format: resolved.format,
|
|
3925
|
+
...syncMarkdownDirectory(harness.id, targetDir, sourceDir, check, options),
|
|
3926
|
+
templates: []
|
|
3927
|
+
};
|
|
3928
|
+
const results = templates.map((template) => syncGeminiTemplate(harness.id, targetDir, template, check, options));
|
|
3929
|
+
const activeNames = new Set(templates.map((template) => `${template.name}.toml`));
|
|
3930
|
+
results.push(...pruneGeminiTarget(harness.id, targetDir, sourceDir, activeNames, check, options));
|
|
3931
|
+
return {
|
|
3932
|
+
id: harness.id,
|
|
3933
|
+
path: targetDir,
|
|
3934
|
+
format: resolved.format,
|
|
3935
|
+
templates: results
|
|
3936
|
+
};
|
|
3937
|
+
}
|
|
3938
|
+
function syncPromptTemplates(harnesses, check = false, options = {}) {
|
|
3939
|
+
const source = join(agntnDataDir(options), "prompts");
|
|
3940
|
+
const templates = readTemplates(source, check);
|
|
3941
|
+
const prepared = harnesses.some((harness) => harness.promptTemplateSyncTarget?.format === "gemini-toml") ? templates.map((template) => ({
|
|
3942
|
+
...template,
|
|
3943
|
+
gemini: geminiContent(template)
|
|
3944
|
+
})) : templates;
|
|
3945
|
+
return {
|
|
3946
|
+
source,
|
|
3947
|
+
check,
|
|
3948
|
+
templates: prepared.map((template) => template.name),
|
|
3949
|
+
targets: harnesses.map((harness) => syncTarget(harness, prepared, source, check, options))
|
|
3137
3950
|
};
|
|
3138
3951
|
}
|
|
3139
|
-
export { Harness, McpEnvError, addMcpServer, detectHarness, detectHarnessFromEnv, detectProjectHarnesses, getAllHarnesses, getHarness, isHarnessId, listHarnesses, listMcpServers, masterMcpPath, parseJsonc, readAgentsConfig, readMasterMcpServers, registerHarness, removeMcpServer, resolvePathTemplate, syncAgentsFiles, syncMcpServers };
|
|
3952
|
+
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
3953
|
|
|
3141
|
-
//# sourceMappingURL=
|
|
3954
|
+
//# sourceMappingURL=prompt-sync.mjs.map
|