@technomoron/agent-run 0.99.28 → 0.99.30
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/CHANGES +26 -0
- package/README.md +202 -26
- package/dist/agents/claude.js +93 -0
- package/dist/agents/codex.js +77 -0
- package/dist/agents/gemini.js +112 -0
- package/dist/agents/grok.js +73 -0
- package/dist/agents/registry.js +26 -0
- package/dist/agents/shared.js +17 -0
- package/dist/agents/types.js +4 -0
- package/dist/checks.js +4 -0
- package/dist/cli.js +45 -12
- package/dist/commands.js +72 -18
- package/dist/config/render-instructions.js +54 -0
- package/dist/config/render-mcp.js +88 -0
- package/dist/config/render-settings.js +35 -0
- package/dist/config/render-skills.js +51 -0
- package/dist/config-tree.js +249 -0
- package/dist/constants.js +5 -2
- package/dist/defaults.js +65 -8
- package/dist/manifest.js +39 -2
- package/dist/renderer.js +81 -227
- package/dist/runtime/environment.js +17 -0
- package/dist/runtime/postflight.js +40 -0
- package/dist/runtime/shared-state.js +44 -0
- package/dist/runtime/spawn-agent.js +7 -0
- package/dist/templates.js +12 -0
- package/examples/basic-config/README.md +2 -1
- package/examples/basic-config/agent-config/agent-run.defaults.jsonc +8 -2
- package/examples/basic-config/agent-config/global/tool-templates/AGENTS.md.njk +2 -0
- package/examples/basic-config/agent-config/global/tool-templates/CLAUDE.md.njk +1 -0
- package/examples/basic-config/agent-config/global/tool-templates/claude-settings.json.njk +2 -0
- package/examples/basic-config/agent-config/global/tool-templates/codex-config.toml.njk +2 -1
- package/examples/basic-config/agent-config/global/tool-templates/gemini-settings.json.njk +5 -0
- package/examples/basic-config/agent-config/global/tool-templates/grok-config.toml.njk +1 -0
- package/examples/basic-config/agent-config/starter/basic-project/agent-run.jsonc +20 -0
- package/examples/basic-config/agent-config/starter/basic-project/notes/memory/README.md +8 -0
- package/examples/basic-config/agent-config/starter/basic-project/overrides/claude-settings.json.njk +2 -0
- package/examples/basic-config/agent-config/starter/basic-project/overrides/codex-config.toml.njk +2 -1
- package/ops/systemd/ai-tools-update.service +1 -1
- package/package.json +1 -1
- package/scripts/install-systemd-jobs.sh +15 -5
- package/scripts/update-ai-tools.sh +27 -16
- package/dist/tools.js +0 -119
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.renderProfileConfig = renderProfileConfig;
|
|
4
|
+
exports.parseJsonObject = parseJsonObject;
|
|
5
|
+
const fs = require("fs");
|
|
6
|
+
const path = require("path");
|
|
7
|
+
const templates_1 = require("../templates");
|
|
8
|
+
const utils_1 = require("../utils");
|
|
9
|
+
function renderProfileConfig(env, configRoot, context, overrideFileName, globalTemplatePath, fallback, label, trace) {
|
|
10
|
+
const templatePath = resolveProfileOverrideTemplate(configRoot, context, overrideFileName, globalTemplatePath);
|
|
11
|
+
const content = templatePath
|
|
12
|
+
? (0, templates_1.renderTemplateFile)(env, configRoot, templatePath, context, trace)
|
|
13
|
+
: fallback();
|
|
14
|
+
(0, templates_1.assertNoUnexpandedTemplateVars)(label, content);
|
|
15
|
+
return content.replace(/\n*$/, '\n');
|
|
16
|
+
}
|
|
17
|
+
function parseJsonObject(content, label) {
|
|
18
|
+
try {
|
|
19
|
+
const parsed = JSON.parse(content);
|
|
20
|
+
if (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed)) {
|
|
21
|
+
throw new Error('top-level value must be an object');
|
|
22
|
+
}
|
|
23
|
+
return parsed;
|
|
24
|
+
}
|
|
25
|
+
catch (error) {
|
|
26
|
+
throw new Error(`invalid generated ${label} JSON: ${(0, utils_1.formatError)(error)}`);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
function resolveProfileOverrideTemplate(configRoot, context, overrideFileName, globalTemplatePath) {
|
|
30
|
+
const overridePath = `${context.profile}/overrides/${overrideFileName}`;
|
|
31
|
+
if (fs.existsSync(path.join(configRoot, overridePath))) {
|
|
32
|
+
return overridePath;
|
|
33
|
+
}
|
|
34
|
+
return fs.existsSync(path.join(configRoot, globalTemplatePath)) ? globalTemplatePath : null;
|
|
35
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.buildCanonicalSkills = buildCanonicalSkills;
|
|
4
|
+
exports.renderSkillFiles = renderSkillFiles;
|
|
5
|
+
exports.validateRenderedSkill = validateRenderedSkill;
|
|
6
|
+
const fs = require("fs");
|
|
7
|
+
const path = require("path");
|
|
8
|
+
const templates_1 = require("../templates");
|
|
9
|
+
function buildCanonicalSkills(env, configRoot, manifest, context, trace) {
|
|
10
|
+
return manifest.skills.install.map((name) => {
|
|
11
|
+
const sourceTemplate = manifest.skills.overrides[name] ?? `global/skills/${name}/SKILL.md.njk`;
|
|
12
|
+
const sourcePath = (0, templates_1.resolveConfigPath)(configRoot, sourceTemplate, context);
|
|
13
|
+
if (!fs.existsSync(sourcePath)) {
|
|
14
|
+
throw new Error(`missing skill template for ${name}: ${sourcePath}`);
|
|
15
|
+
}
|
|
16
|
+
const renderedContent = (0, templates_1.renderTemplateFile)(env, configRoot, sourceTemplate, context, trace);
|
|
17
|
+
(0, templates_1.assertNoUnexpandedTemplateVars)(`skill ${name}`, renderedContent);
|
|
18
|
+
validateRenderedSkill(renderedContent, sourcePath);
|
|
19
|
+
return { name, sourcePath, renderedContent, description: extractSkillDescription(renderedContent) };
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
function renderSkillFiles(context, targetDir) {
|
|
23
|
+
return context.skills.map((skill) => ({
|
|
24
|
+
path: path.join(targetDir, skill.name, 'SKILL.md'),
|
|
25
|
+
content: skill.renderedContent
|
|
26
|
+
}));
|
|
27
|
+
}
|
|
28
|
+
function validateRenderedSkill(content, sourcePath) {
|
|
29
|
+
const frontMatter = /^---\n([\s\S]*?)\n---\n/.exec(content.replace(/\r\n/g, '\n'));
|
|
30
|
+
if (frontMatter === null) {
|
|
31
|
+
throw new Error(`generated skill missing YAML front matter: ${sourcePath}`);
|
|
32
|
+
}
|
|
33
|
+
const yaml = frontMatter[1] ?? '';
|
|
34
|
+
if (!/^name:\s*\S+/m.test(yaml)) {
|
|
35
|
+
throw new Error(`generated skill missing name: ${sourcePath}`);
|
|
36
|
+
}
|
|
37
|
+
if (!/^description:\s*(?:\S|>\s*$)/m.test(yaml)) {
|
|
38
|
+
throw new Error(`generated skill missing description: ${sourcePath}`);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
function extractSkillDescription(content) {
|
|
42
|
+
const normalized = content.replace(/\r\n/g, '\n');
|
|
43
|
+
const simple = /^description:\s*['"]?(.+?)['"]?\s*$/m.exec(normalized);
|
|
44
|
+
if (simple?.[1]) {
|
|
45
|
+
return simple[1].trim();
|
|
46
|
+
}
|
|
47
|
+
const folded = /^description:\s*>\s*\n((?:[ \t]+.+\n?)+)/m.exec(normalized);
|
|
48
|
+
return folded?.[1]
|
|
49
|
+
? folded[1].split('\n').map((line) => line.trim()).filter(Boolean).join(' ')
|
|
50
|
+
: '';
|
|
51
|
+
}
|
package/dist/config-tree.js
CHANGED
|
@@ -3,14 +3,18 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.ensureConfigRootLayout = ensureConfigRootLayout;
|
|
4
4
|
exports.ensureConfigRootGitignore = ensureConfigRootGitignore;
|
|
5
5
|
exports.ensureDefaultGlobalTemplates = ensureDefaultGlobalTemplates;
|
|
6
|
+
exports.ensurePortableSystemdFiles = ensurePortableSystemdFiles;
|
|
6
7
|
exports.migrateOldTemplates = migrateOldTemplates;
|
|
7
8
|
exports.findLegacyProfileDirs = findLegacyProfileDirs;
|
|
8
9
|
exports.findProfileDirs = findProfileDirs;
|
|
9
10
|
exports.convertLegacyTemplateVars = convertLegacyTemplateVars;
|
|
10
11
|
exports.migrateCodexRuntimeFiles = migrateCodexRuntimeFiles;
|
|
11
12
|
exports.migrateLooseFiles = migrateLooseFiles;
|
|
13
|
+
exports.describeLegacyProfileLayout = describeLegacyProfileLayout;
|
|
14
|
+
exports.migrateProfileLayout = migrateProfileLayout;
|
|
12
15
|
exports.starterConfigRootPath = starterConfigRootPath;
|
|
13
16
|
exports.copySkeletonTree = copySkeletonTree;
|
|
17
|
+
const childProcess = require("child_process");
|
|
14
18
|
const fs = require("fs");
|
|
15
19
|
const path = require("path");
|
|
16
20
|
const constants_1 = require("./constants");
|
|
@@ -68,6 +72,25 @@ function ensureDefaultGlobalTemplates(configRoot) {
|
|
|
68
72
|
}
|
|
69
73
|
}
|
|
70
74
|
}
|
|
75
|
+
function ensurePortableSystemdFiles(configRoot) {
|
|
76
|
+
const packageRoot = path.resolve(__dirname, '..');
|
|
77
|
+
const files = new Map([
|
|
78
|
+
['install-systemd-jobs.sh', { source: 'scripts/install-systemd-jobs.sh', mode: 0o755 }],
|
|
79
|
+
['update-ai-tools.sh', { source: 'scripts/update-ai-tools.sh', mode: 0o755 }],
|
|
80
|
+
['ai-tools-update.service', { source: 'ops/systemd/ai-tools-update.service', mode: 0o644 }],
|
|
81
|
+
['ai-tools-update.timer', { source: 'ops/systemd/ai-tools-update.timer', mode: 0o644 }]
|
|
82
|
+
]);
|
|
83
|
+
for (const [targetName, asset] of files) {
|
|
84
|
+
const sourcePath = path.join(packageRoot, asset.source);
|
|
85
|
+
const targetPath = path.join(configRoot, targetName);
|
|
86
|
+
if (!fs.existsSync(sourcePath)) {
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
fs.copyFileSync(sourcePath, targetPath);
|
|
90
|
+
fs.chmodSync(targetPath, asset.mode);
|
|
91
|
+
(0, utils_1.verbose)(`updated ${targetPath}`);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
71
94
|
function migrateOldTemplates(configRoot) {
|
|
72
95
|
const oldCodeTemplate = path.join(configRoot, 'templates', 'AGENTS-CODE.md');
|
|
73
96
|
const newCodeTemplate = path.join(configRoot, 'global', 'agents', 'code.md.njk');
|
|
@@ -150,6 +173,232 @@ function migrateLooseFiles(agentDir, pattern, targetDirName) {
|
|
|
150
173
|
}
|
|
151
174
|
return moved;
|
|
152
175
|
}
|
|
176
|
+
function describeLegacyProfileLayout(profileDir) {
|
|
177
|
+
const paths = [];
|
|
178
|
+
const legacyMemoryDir = path.join(profileDir, 'memory');
|
|
179
|
+
if (fs.existsSync(legacyMemoryDir)) {
|
|
180
|
+
paths.push(legacyMemoryDir);
|
|
181
|
+
}
|
|
182
|
+
for (const filePath of legacyProjectMemoryFiles(profileDir)) {
|
|
183
|
+
paths.push(filePath);
|
|
184
|
+
}
|
|
185
|
+
const legacyCodexHome = path.join(profileDir, 'memories', 'codex-home');
|
|
186
|
+
if (fs.existsSync(legacyCodexHome) || (0, utils_1.isSymlink)(legacyCodexHome)) {
|
|
187
|
+
paths.push(legacyCodexHome);
|
|
188
|
+
}
|
|
189
|
+
return [...new Set(paths)].sort();
|
|
190
|
+
}
|
|
191
|
+
function migrateProfileLayout(configRoot, profileDir, confirmGitMoves) {
|
|
192
|
+
const projectMoves = planProjectMemoryMoves(profileDir);
|
|
193
|
+
validateMoveTargets(projectMoves);
|
|
194
|
+
const git = findGitWorktree(configRoot);
|
|
195
|
+
const trackedMoves = git === null ? [] : projectMoves.filter((move) => isTrackedByGit(git, move.source));
|
|
196
|
+
if (git !== null && trackedMoves.length > 0 && !confirmGitMoves({ gitRoot: git.root, moves: trackedMoves })) {
|
|
197
|
+
throw new Error('profile layout migration declined; no files were moved');
|
|
198
|
+
}
|
|
199
|
+
let gitMoves = 0;
|
|
200
|
+
for (const move of projectMoves) {
|
|
201
|
+
fs.mkdirSync(path.dirname(move.target), { recursive: true });
|
|
202
|
+
if (git !== null && trackedMoves.includes(move)) {
|
|
203
|
+
runGitMove(git, move);
|
|
204
|
+
gitMoves += 1;
|
|
205
|
+
}
|
|
206
|
+
else {
|
|
207
|
+
fs.renameSync(move.source, move.target);
|
|
208
|
+
}
|
|
209
|
+
removeEmptyParents(path.dirname(move.source), profileDir);
|
|
210
|
+
(0, utils_1.verbose)(`moved project memory ${move.source} -> ${move.target}`);
|
|
211
|
+
}
|
|
212
|
+
removeEmptyParents(path.join(profileDir, 'memory'), profileDir);
|
|
213
|
+
removeEmptyParents(path.join(profileDir, 'memories'), profileDir);
|
|
214
|
+
const runtimeMoves = migrateLegacyCodexHome(profileDir);
|
|
215
|
+
return { gitMoves, projectMemoryMoves: projectMoves.length, runtimeMoves };
|
|
216
|
+
}
|
|
217
|
+
function planProjectMemoryMoves(profileDir) {
|
|
218
|
+
const targetDir = path.join(profileDir, 'notes', 'memory');
|
|
219
|
+
const moves = [];
|
|
220
|
+
const legacyMemoryDir = path.join(profileDir, 'memory');
|
|
221
|
+
for (const source of listTreeFiles(legacyMemoryDir)) {
|
|
222
|
+
moves.push({ source, target: path.join(targetDir, path.relative(legacyMemoryDir, source)) });
|
|
223
|
+
}
|
|
224
|
+
for (const source of legacyProjectMemoryFiles(profileDir)) {
|
|
225
|
+
moves.push({ source, target: path.join(targetDir, path.basename(source)) });
|
|
226
|
+
}
|
|
227
|
+
return moves.sort((left, right) => left.source.localeCompare(right.source));
|
|
228
|
+
}
|
|
229
|
+
function legacyProjectMemoryFiles(profileDir) {
|
|
230
|
+
const files = [];
|
|
231
|
+
for (const entry of safeReadDir(profileDir)) {
|
|
232
|
+
if (entry.isFile() && /^memory.*\.md$/i.test(entry.name)) {
|
|
233
|
+
files.push(path.join(profileDir, entry.name));
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
const legacyMemoriesDir = path.join(profileDir, 'memories');
|
|
237
|
+
for (const entry of safeReadDir(legacyMemoriesDir)) {
|
|
238
|
+
if (entry.isFile() && /\.md$/i.test(entry.name)) {
|
|
239
|
+
files.push(path.join(legacyMemoriesDir, entry.name));
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
return files.sort();
|
|
243
|
+
}
|
|
244
|
+
function safeReadDir(dir) {
|
|
245
|
+
try {
|
|
246
|
+
return fs.readdirSync(dir, { withFileTypes: true });
|
|
247
|
+
}
|
|
248
|
+
catch (error) {
|
|
249
|
+
if (error instanceof Error && 'code' in error && error.code === 'ENOENT') {
|
|
250
|
+
return [];
|
|
251
|
+
}
|
|
252
|
+
throw error;
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
function listTreeFiles(root) {
|
|
256
|
+
const files = [];
|
|
257
|
+
for (const entry of safeReadDir(root)) {
|
|
258
|
+
const filePath = path.join(root, entry.name);
|
|
259
|
+
if (entry.isDirectory()) {
|
|
260
|
+
files.push(...listTreeFiles(filePath));
|
|
261
|
+
}
|
|
262
|
+
else if (entry.isFile() || entry.isSymbolicLink()) {
|
|
263
|
+
files.push(filePath);
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
return files;
|
|
267
|
+
}
|
|
268
|
+
function validateMoveTargets(moves) {
|
|
269
|
+
const targets = new Set();
|
|
270
|
+
for (const move of moves) {
|
|
271
|
+
const target = path.resolve(move.target);
|
|
272
|
+
if (targets.has(target) || fs.existsSync(target) || (0, utils_1.isSymlink)(target)) {
|
|
273
|
+
throw new Error(`cannot migrate project memory because the destination already exists: ${move.target}`);
|
|
274
|
+
}
|
|
275
|
+
targets.add(target);
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
function findGitWorktree(configRoot) {
|
|
279
|
+
const searchPath = process.env.AGENT_RUN_REAL_PATH ?? process.env.PATH ?? '';
|
|
280
|
+
const gitCommand = findCommandOnPath('git', searchPath);
|
|
281
|
+
if (gitCommand === null) {
|
|
282
|
+
return null;
|
|
283
|
+
}
|
|
284
|
+
const env = { ...process.env, PATH: searchPath };
|
|
285
|
+
const result = childProcess.spawnSync(gitCommand, ['-C', configRoot, 'rev-parse', '--show-toplevel'], {
|
|
286
|
+
encoding: 'utf8',
|
|
287
|
+
env,
|
|
288
|
+
shell: false
|
|
289
|
+
});
|
|
290
|
+
if (result.status !== 0) {
|
|
291
|
+
return null;
|
|
292
|
+
}
|
|
293
|
+
const root = result.stdout.trim();
|
|
294
|
+
return root ? { command: gitCommand, env, root: path.resolve(root) } : null;
|
|
295
|
+
}
|
|
296
|
+
function findCommandOnPath(command, searchPath) {
|
|
297
|
+
const extensions = process.platform === 'win32' ? ['.exe', '.cmd', '.bat', ''] : [''];
|
|
298
|
+
for (const dir of searchPath.split(path.delimiter).filter(Boolean)) {
|
|
299
|
+
for (const extension of extensions) {
|
|
300
|
+
const candidate = path.join(dir, `${command}${extension}`);
|
|
301
|
+
try {
|
|
302
|
+
fs.accessSync(candidate, process.platform === 'win32' ? fs.constants.F_OK : fs.constants.X_OK);
|
|
303
|
+
return candidate;
|
|
304
|
+
}
|
|
305
|
+
catch {
|
|
306
|
+
continue;
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
return null;
|
|
311
|
+
}
|
|
312
|
+
function isTrackedByGit(git, filePath) {
|
|
313
|
+
const relativePath = path.relative(git.root, filePath).replace(/\\/g, '/');
|
|
314
|
+
const result = childProcess.spawnSync(git.command, ['-C', git.root, '--literal-pathspecs', 'ls-files', '--error-unmatch', '--', relativePath], { encoding: 'utf8', env: git.env, shell: false });
|
|
315
|
+
return result.status === 0;
|
|
316
|
+
}
|
|
317
|
+
function runGitMove(git, move) {
|
|
318
|
+
const source = path.relative(git.root, move.source).replace(/\\/g, '/');
|
|
319
|
+
const target = path.relative(git.root, move.target).replace(/\\/g, '/');
|
|
320
|
+
const result = childProcess.spawnSync(git.command, ['-C', git.root, '--literal-pathspecs', 'mv', '--', source, target], {
|
|
321
|
+
encoding: 'utf8',
|
|
322
|
+
env: git.env,
|
|
323
|
+
shell: false
|
|
324
|
+
});
|
|
325
|
+
if (result.status !== 0) {
|
|
326
|
+
const detail = result.stderr.trim() || result.stdout.trim() || `git exited with status ${result.status}`;
|
|
327
|
+
throw new Error(`cannot migrate tracked project memory ${move.source}: ${detail}`);
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
function migrateLegacyCodexHome(profileDir) {
|
|
331
|
+
const source = path.join(profileDir, 'memories', 'codex-home');
|
|
332
|
+
if (!fs.existsSync(source) && !(0, utils_1.isSymlink)(source)) {
|
|
333
|
+
return 0;
|
|
334
|
+
}
|
|
335
|
+
const target = path.join(profileDir, constants_1.LIVE_DIR_NAME, 'memories', 'codex-home');
|
|
336
|
+
if (!fs.existsSync(target) && !(0, utils_1.isSymlink)(target)) {
|
|
337
|
+
fs.mkdirSync(path.dirname(target), { recursive: true });
|
|
338
|
+
fs.renameSync(source, target);
|
|
339
|
+
removeEmptyParents(path.dirname(source), profileDir);
|
|
340
|
+
(0, utils_1.verbose)(`moved Codex runtime ${source} -> ${target}`);
|
|
341
|
+
return 1;
|
|
342
|
+
}
|
|
343
|
+
const sourceIsDirectory = !(0, utils_1.isSymlink)(source) && fs.statSync(source).isDirectory();
|
|
344
|
+
const targetIsDirectory = !(0, utils_1.isSymlink)(target) && fs.statSync(target).isDirectory();
|
|
345
|
+
if (!sourceIsDirectory || !targetIsDirectory) {
|
|
346
|
+
const backupTarget = (0, utils_1.nextBackupPath)(target);
|
|
347
|
+
fs.renameSync(source, backupTarget);
|
|
348
|
+
removeEmptyParents(path.dirname(source), profileDir);
|
|
349
|
+
(0, utils_1.verbose)(`moved Codex runtime ${source} -> ${backupTarget}`);
|
|
350
|
+
return 1;
|
|
351
|
+
}
|
|
352
|
+
const moved = mergeRuntimeTree(source, target);
|
|
353
|
+
removeEmptyParents(path.dirname(source), profileDir);
|
|
354
|
+
return moved;
|
|
355
|
+
}
|
|
356
|
+
function mergeRuntimeTree(sourceDir, targetDir) {
|
|
357
|
+
let moved = 0;
|
|
358
|
+
fs.mkdirSync(targetDir, { recursive: true });
|
|
359
|
+
for (const entry of safeReadDir(sourceDir)) {
|
|
360
|
+
const source = path.join(sourceDir, entry.name);
|
|
361
|
+
let target = path.join(targetDir, entry.name);
|
|
362
|
+
if (entry.isDirectory() && !entry.isSymbolicLink()) {
|
|
363
|
+
const targetIsDirectory = fs.existsSync(target) && !(0, utils_1.isSymlink)(target) && fs.statSync(target).isDirectory();
|
|
364
|
+
if ((fs.existsSync(target) || (0, utils_1.isSymlink)(target)) && !targetIsDirectory) {
|
|
365
|
+
target = (0, utils_1.nextBackupPath)(target);
|
|
366
|
+
}
|
|
367
|
+
moved += mergeRuntimeTree(source, target);
|
|
368
|
+
continue;
|
|
369
|
+
}
|
|
370
|
+
const resolvedTarget = fs.existsSync(target) || (0, utils_1.isSymlink)(target) ? (0, utils_1.nextBackupPath)(target) : target;
|
|
371
|
+
fs.mkdirSync(path.dirname(resolvedTarget), { recursive: true });
|
|
372
|
+
fs.renameSync(source, resolvedTarget);
|
|
373
|
+
(0, utils_1.verbose)(`moved Codex runtime ${source} -> ${resolvedTarget}`);
|
|
374
|
+
moved += 1;
|
|
375
|
+
}
|
|
376
|
+
try {
|
|
377
|
+
fs.rmdirSync(sourceDir);
|
|
378
|
+
}
|
|
379
|
+
catch (error) {
|
|
380
|
+
if (!(error instanceof Error && 'code' in error && error.code === 'ENOENT')) {
|
|
381
|
+
throw error;
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
return moved;
|
|
385
|
+
}
|
|
386
|
+
function removeEmptyParents(startDir, stopDir) {
|
|
387
|
+
let dir = path.resolve(startDir);
|
|
388
|
+
const stop = path.resolve(stopDir);
|
|
389
|
+
while (dir !== stop && (0, utils_1.isSamePathOrDescendant)(dir, stop)) {
|
|
390
|
+
try {
|
|
391
|
+
fs.rmdirSync(dir);
|
|
392
|
+
}
|
|
393
|
+
catch (error) {
|
|
394
|
+
if (error instanceof Error && 'code' in error && ['ENOENT', 'ENOTEMPTY'].includes(String(error.code))) {
|
|
395
|
+
return;
|
|
396
|
+
}
|
|
397
|
+
throw error;
|
|
398
|
+
}
|
|
399
|
+
dir = path.dirname(dir);
|
|
400
|
+
}
|
|
401
|
+
}
|
|
153
402
|
function starterConfigRootPath() {
|
|
154
403
|
return path.resolve(__dirname, '..', 'examples', 'basic-config', 'agent-config');
|
|
155
404
|
}
|
package/dist/constants.js
CHANGED
|
@@ -11,7 +11,7 @@ exports.LIVE_DIR_NAME = 'live';
|
|
|
11
11
|
exports.CONFIG_ROOT_OVERRIDE_ENV = 'AGENT_RUN_CONFIG_ROOT_OVERRIDE';
|
|
12
12
|
exports.CONFIG_DIR_ENV = 'AGENT_CONFIG_DIR';
|
|
13
13
|
exports.VERBOSE_ENV = 'AGENT_RUN_VERBOSE';
|
|
14
|
-
exports.PACKAGE_VERSION = '0.99.
|
|
14
|
+
exports.PACKAGE_VERSION = '0.99.30';
|
|
15
15
|
exports.UNEXPANDED_TEMPLATE_RE = /\{\{[^}]+\}\}|\{%[^%]+%\}/;
|
|
16
16
|
exports.LOCAL_AI_FILE_NAMES = new Set([
|
|
17
17
|
'AGENTS.md',
|
|
@@ -19,10 +19,11 @@ exports.LOCAL_AI_FILE_NAMES = new Set([
|
|
|
19
19
|
'AGENTS.override.md',
|
|
20
20
|
'CLAUDE.md',
|
|
21
21
|
'CLAUDE.local.md',
|
|
22
|
+
'GEMINI.md',
|
|
22
23
|
'.mcp.json',
|
|
23
24
|
'codex.md'
|
|
24
25
|
]);
|
|
25
|
-
exports.LOCAL_AI_DIRECTORY_NAMES = new Set(['.agents', '.claude', '.codex']);
|
|
26
|
+
exports.LOCAL_AI_DIRECTORY_NAMES = new Set(['.agents', '.claude', '.codex', '.gemini', '.grok']);
|
|
26
27
|
exports.GENERATED_GITIGNORE_ENTRIES = [
|
|
27
28
|
'# Generated agent-run live profiles',
|
|
28
29
|
'**/live/',
|
|
@@ -55,6 +56,8 @@ exports.REQUIRED_GLOBAL_TEMPLATES = [
|
|
|
55
56
|
'global/snippets/verification.md.njk',
|
|
56
57
|
'global/tool-templates/codex-config.toml.njk',
|
|
57
58
|
'global/tool-templates/claude-settings.json.njk',
|
|
59
|
+
'global/tool-templates/gemini-settings.json.njk',
|
|
60
|
+
'global/tool-templates/grok-config.toml.njk',
|
|
58
61
|
'global/skills/commit-workflow/SKILL.md.njk',
|
|
59
62
|
'global/skills/github-release/SKILL.md.njk',
|
|
60
63
|
'global/skills/release-package-check/SKILL.md.njk',
|
package/dist/defaults.js
CHANGED
|
@@ -3,10 +3,13 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.defaultManifest = defaultManifest;
|
|
4
4
|
exports.stringifyRootDefaults = stringifyRootDefaults;
|
|
5
5
|
exports.defaultLocalTemplate = defaultLocalTemplate;
|
|
6
|
+
exports.defaultProjectMemoryIndex = defaultProjectMemoryIndex;
|
|
6
7
|
exports.defaultGlobalTemplates = defaultGlobalTemplates;
|
|
7
8
|
exports.defaultToolInstructionsTemplate = defaultToolInstructionsTemplate;
|
|
8
9
|
exports.defaultCodexConfigContent = defaultCodexConfigContent;
|
|
9
10
|
exports.defaultClaudeSettingsContent = defaultClaudeSettingsContent;
|
|
11
|
+
exports.defaultGeminiSettingsContent = defaultGeminiSettingsContent;
|
|
12
|
+
exports.defaultGrokConfigContent = defaultGrokConfigContent;
|
|
10
13
|
exports.legacyDefaultClaudeSettingsContent = legacyDefaultClaudeSettingsContent;
|
|
11
14
|
function defaultManifest(profile) {
|
|
12
15
|
return {
|
|
@@ -21,8 +24,11 @@ function defaultManifest(profile) {
|
|
|
21
24
|
},
|
|
22
25
|
tools: {
|
|
23
26
|
codex: true,
|
|
24
|
-
claude: true
|
|
27
|
+
claude: true,
|
|
28
|
+
gemini: true,
|
|
29
|
+
grok: true
|
|
25
30
|
},
|
|
31
|
+
mcp: { servers: {} },
|
|
26
32
|
checks: ['agent-run check .', 'pnpm run cleanbuild'],
|
|
27
33
|
guardrails: {
|
|
28
34
|
blockGitWrite: true,
|
|
@@ -34,7 +40,8 @@ function defaultManifest(profile) {
|
|
|
34
40
|
changesFile: '{{ projectRoot }}/CHANGES',
|
|
35
41
|
reviewDir: '{{ profileDir }}/reviews',
|
|
36
42
|
reviewFile: '{{ profileDir }}/reviews/REVIEW-{{ date }}.md',
|
|
37
|
-
memoriesDir: '{{ agentDir }}/memories'
|
|
43
|
+
memoriesDir: '{{ agentDir }}/memories',
|
|
44
|
+
projectMemoryDir: '{{ profileDir }}/notes/memory'
|
|
38
45
|
}
|
|
39
46
|
};
|
|
40
47
|
}
|
|
@@ -71,6 +78,19 @@ function defaultLocalTemplate() {
|
|
|
71
78
|
''
|
|
72
79
|
].join('\n');
|
|
73
80
|
}
|
|
81
|
+
function defaultProjectMemoryIndex() {
|
|
82
|
+
return [
|
|
83
|
+
'# Project memory',
|
|
84
|
+
'',
|
|
85
|
+
'This directory contains durable notes for this project across agent sessions and machines.',
|
|
86
|
+
'Keep the notes short, factual, and free of secrets. Link additional topic files from this index.',
|
|
87
|
+
'',
|
|
88
|
+
'## Files',
|
|
89
|
+
'',
|
|
90
|
+
'- Add project memory files here as they are needed.',
|
|
91
|
+
''
|
|
92
|
+
].join('\n');
|
|
93
|
+
}
|
|
74
94
|
function defaultGlobalTemplates() {
|
|
75
95
|
return new Map([
|
|
76
96
|
['global/agents/code.md.njk', defaultCodeAgentTemplate()],
|
|
@@ -80,6 +100,8 @@ function defaultGlobalTemplates() {
|
|
|
80
100
|
['global/snippets/verification.md.njk', defaultVerificationSnippet()],
|
|
81
101
|
['global/tool-templates/codex-config.toml.njk', defaultCodexConfigTemplate()],
|
|
82
102
|
['global/tool-templates/claude-settings.json.njk', defaultClaudeSettingsTemplate()],
|
|
103
|
+
['global/tool-templates/gemini-settings.json.njk', defaultGeminiSettingsTemplate()],
|
|
104
|
+
['global/tool-templates/grok-config.toml.njk', defaultGrokConfigTemplate()],
|
|
83
105
|
['global/skills/commit-workflow/SKILL.md.njk', defaultCommitWorkflowSkill()],
|
|
84
106
|
['global/skills/github-release/SKILL.md.njk', defaultGithubReleaseSkill()],
|
|
85
107
|
['global/skills/release-package-check/SKILL.md.njk', defaultReleasePackageCheckSkill()],
|
|
@@ -123,7 +145,7 @@ function defaultNoAiFilesSnippet() {
|
|
|
123
145
|
return [
|
|
124
146
|
'## Agent File Storage',
|
|
125
147
|
'',
|
|
126
|
-
'Do not create AGENTS.md, CLAUDE.md, .agents, .claude, .codex, or other agent runtime files inside the project repository.',
|
|
148
|
+
'Do not create AGENTS.md, CLAUDE.md, GEMINI.md, .agents, .claude, .codex, .gemini, .grok, or other agent runtime files inside the project repository.',
|
|
127
149
|
'Generated agent-only files belong under {{ paths.liveDir }}.',
|
|
128
150
|
'Source config files belong under {{ profileDir }}.',
|
|
129
151
|
''
|
|
@@ -196,7 +218,7 @@ function defaultReleasePackageCheckSkill() {
|
|
|
196
218
|
'- The year in the top `CHANGES` version line appears in `package.json.copyright`.',
|
|
197
219
|
'- The git `origin` remote matches `<org>/<repo>`.',
|
|
198
220
|
'- The current branch has an upstream configured and is in sync with it after fetching.',
|
|
199
|
-
'- The repository does not contain agent files or directories such as `AGENTS.md`, `CLAUDE.md`, `codex.md`, `.claude`, or `.
|
|
221
|
+
'- The repository does not contain agent files or directories such as `AGENTS.md`, `CLAUDE.md`, `GEMINI.md`, `codex.md`, `.claude`, `.codex`, `.gemini`, or `.grok`.',
|
|
200
222
|
'- The repository root has a lockfile: `pnpm-lock.yaml`, `package-lock.json`, `yarn.lock`, `bun.lockb`, or `bun.lock`.',
|
|
201
223
|
'- Every GitHub Actions `uses:` reference is pinned to a full commit SHA.',
|
|
202
224
|
'- The pinned `actions/setup-node` release is v6-compatible and workflows use `node-version: 24`.',
|
|
@@ -293,6 +315,18 @@ function defaultToolInstructionsTemplate(isClaude) {
|
|
|
293
315
|
'{{ paths.memoriesDir }}',
|
|
294
316
|
'```',
|
|
295
317
|
'',
|
|
318
|
+
'Project memory directory:',
|
|
319
|
+
'',
|
|
320
|
+
'```text',
|
|
321
|
+
'{{ paths.projectMemoryDir }}',
|
|
322
|
+
'```',
|
|
323
|
+
'',
|
|
324
|
+
'Native Codex memory directory:',
|
|
325
|
+
'',
|
|
326
|
+
'```text',
|
|
327
|
+
'{{ paths.nativeMemoryDir }}',
|
|
328
|
+
'```',
|
|
329
|
+
'',
|
|
296
330
|
'Global memory directory:',
|
|
297
331
|
'',
|
|
298
332
|
'```text',
|
|
@@ -315,11 +349,11 @@ function defaultToolInstructionsTemplate(isClaude) {
|
|
|
315
349
|
'',
|
|
316
350
|
isClaude
|
|
317
351
|
? 'Claude receives {{ agentDir }}/CLAUDE.md through `--append-system-prompt-file` and loads generated skills as a local plugin.'
|
|
318
|
-
: 'Codex
|
|
352
|
+
: 'Codex, Gemini, and Grok load this canonical AGENTS.md content from their private agent-run runtimes.',
|
|
319
353
|
'',
|
|
320
354
|
'## Mandatory Path Rule',
|
|
321
355
|
'',
|
|
322
|
-
'Do not create AGENTS.md, CLAUDE.md, .agents, .claude, .codex, or AI-related files inside the project repository.',
|
|
356
|
+
'Do not create AGENTS.md, CLAUDE.md, GEMINI.md, .agents, .claude, .codex, .gemini, .grok, or AI-related files inside the project repository.',
|
|
323
357
|
'Generated agent-only files must be stored under the agent directory shown above.',
|
|
324
358
|
'Source config files must be stored under the profile source directory shown above.',
|
|
325
359
|
''
|
|
@@ -337,7 +371,8 @@ function defaultCodexConfigTemplate() {
|
|
|
337
371
|
'writable_roots = [',
|
|
338
372
|
' {{ projectRoot | dump }},',
|
|
339
373
|
' {{ paths.reviewDir | dump }},',
|
|
340
|
-
' {{ paths.memoriesDir | dump }}',
|
|
374
|
+
' {{ paths.memoriesDir | dump }},',
|
|
375
|
+
' {{ paths.projectMemoryDir | dump }}',
|
|
341
376
|
']',
|
|
342
377
|
'network_access = false',
|
|
343
378
|
''
|
|
@@ -349,6 +384,7 @@ function defaultClaudeSettingsTemplate() {
|
|
|
349
384
|
' "AGENT_DIR": {{ agentDir | dump }},',
|
|
350
385
|
' "AGENT_PROFILE_DIR": {{ profileDir | dump }},',
|
|
351
386
|
' "AGENT_RUN_PROJECT_ROOT": {{ projectRoot | dump }},',
|
|
387
|
+
' "AGENT_PROJECT_MEMORY_DIR": {{ paths.projectMemoryDir | dump }},',
|
|
352
388
|
' "AGENT_GLOBAL_MEMORY_DIR": {{ paths.globalMemoryDir | dump }}',
|
|
353
389
|
' }'
|
|
354
390
|
].join('\n');
|
|
@@ -365,6 +401,19 @@ function defaultClaudeSettingsTemplate() {
|
|
|
365
401
|
'{% endif %}'
|
|
366
402
|
].join('\n');
|
|
367
403
|
}
|
|
404
|
+
function defaultGeminiSettingsTemplate() {
|
|
405
|
+
return [
|
|
406
|
+
'{',
|
|
407
|
+
' "general": {',
|
|
408
|
+
' "enableAutoUpdate": true',
|
|
409
|
+
' }',
|
|
410
|
+
'}',
|
|
411
|
+
''
|
|
412
|
+
].join('\n');
|
|
413
|
+
}
|
|
414
|
+
function defaultGrokConfigTemplate() {
|
|
415
|
+
return ['# Generated by agent-run. Do not edit directly.', ''].join('\n');
|
|
416
|
+
}
|
|
368
417
|
function defaultCodexConfigContent(context) {
|
|
369
418
|
return [
|
|
370
419
|
'# Generated by agent-run. Do not edit directly.',
|
|
@@ -377,7 +426,8 @@ function defaultCodexConfigContent(context) {
|
|
|
377
426
|
'writable_roots = [',
|
|
378
427
|
` ${JSON.stringify(context.projectRoot)},`,
|
|
379
428
|
` ${JSON.stringify(context.paths.reviewDir)},`,
|
|
380
|
-
` ${JSON.stringify(context.paths.memoriesDir)}
|
|
429
|
+
` ${JSON.stringify(context.paths.memoriesDir)},`,
|
|
430
|
+
` ${JSON.stringify(context.paths.projectMemoryDir)}`,
|
|
381
431
|
']',
|
|
382
432
|
'network_access = false',
|
|
383
433
|
''
|
|
@@ -389,6 +439,7 @@ function defaultClaudeSettingsContent(context) {
|
|
|
389
439
|
AGENT_DIR: context.agentDir,
|
|
390
440
|
AGENT_PROFILE_DIR: context.profileDir,
|
|
391
441
|
AGENT_RUN_PROJECT_ROOT: context.projectRoot,
|
|
442
|
+
AGENT_PROJECT_MEMORY_DIR: context.paths.projectMemoryDir,
|
|
392
443
|
AGENT_GLOBAL_MEMORY_DIR: context.paths.globalMemoryDir
|
|
393
444
|
}
|
|
394
445
|
};
|
|
@@ -397,6 +448,12 @@ function defaultClaudeSettingsContent(context) {
|
|
|
397
448
|
}
|
|
398
449
|
return `${JSON.stringify(settings, null, 2)}\n`;
|
|
399
450
|
}
|
|
451
|
+
function defaultGeminiSettingsContent(_context) {
|
|
452
|
+
return `${JSON.stringify({ general: { enableAutoUpdate: true } }, null, 2)}\n`;
|
|
453
|
+
}
|
|
454
|
+
function defaultGrokConfigContent() {
|
|
455
|
+
return '# Generated by agent-run. Do not edit directly.\n';
|
|
456
|
+
}
|
|
400
457
|
function legacyDefaultClaudeSettingsContent(context, agentDir) {
|
|
401
458
|
return `${JSON.stringify({
|
|
402
459
|
env: {
|
package/dist/manifest.js
CHANGED
|
@@ -100,6 +100,11 @@ function mergeManifest(base, override, allowProfile, inferredProfile) {
|
|
|
100
100
|
agent: { ...base.agent, ...override.agent },
|
|
101
101
|
skills: mergeSkills(base.skills, override.skills),
|
|
102
102
|
tools: { ...base.tools, ...override.tools },
|
|
103
|
+
mcp: {
|
|
104
|
+
...base.mcp,
|
|
105
|
+
...override.mcp,
|
|
106
|
+
servers: { ...base.mcp?.servers, ...override.mcp?.servers }
|
|
107
|
+
},
|
|
103
108
|
guardrails: { ...base.guardrails, ...override.guardrails },
|
|
104
109
|
paths: { ...base.paths, ...override.paths }
|
|
105
110
|
};
|
|
@@ -151,8 +156,11 @@ function normalizeManifest(manifest, profile) {
|
|
|
151
156
|
},
|
|
152
157
|
tools: {
|
|
153
158
|
codex: manifest.tools?.codex ?? true,
|
|
154
|
-
claude: manifest.tools?.claude ?? true
|
|
159
|
+
claude: manifest.tools?.claude ?? true,
|
|
160
|
+
gemini: manifest.tools?.gemini ?? true,
|
|
161
|
+
grok: manifest.tools?.grok ?? true
|
|
155
162
|
},
|
|
163
|
+
mcpServers: normalizeMcpServers(manifest.mcp?.servers ?? {}),
|
|
156
164
|
checks: manifest.checks ?? fallback.checks ?? [],
|
|
157
165
|
guardrails: {
|
|
158
166
|
blockGitWrite: manifest.guardrails?.blockGitWrite ?? true,
|
|
@@ -165,10 +173,39 @@ function normalizeManifest(manifest, profile) {
|
|
|
165
173
|
reviewDir: normalizeLegacyReviewPath(paths.reviewDir ?? fallbackPaths.reviewDir ?? '{{ profileDir }}/reviews'),
|
|
166
174
|
reviewFile: normalizeLegacyReviewPath(paths.reviewFile ?? fallbackPaths.reviewFile ?? '{{ profileDir }}/reviews/REVIEW-{{ date }}.md'),
|
|
167
175
|
reviewConsolidatedFile: paths.reviewConsolidatedFile ?? fallbackPaths.reviewConsolidatedFile,
|
|
168
|
-
memoriesDir: paths.memoriesDir ?? fallbackPaths.memoriesDir ?? '{{ agentDir }}/memories'
|
|
176
|
+
memoriesDir: paths.memoriesDir ?? fallbackPaths.memoriesDir ?? '{{ agentDir }}/memories',
|
|
177
|
+
projectMemoryDir: paths.projectMemoryDir ?? fallbackPaths.projectMemoryDir ?? '{{ profileDir }}/notes/memory'
|
|
169
178
|
}
|
|
170
179
|
};
|
|
171
180
|
}
|
|
181
|
+
function normalizeMcpServers(servers) {
|
|
182
|
+
const normalized = {};
|
|
183
|
+
for (const [name, server] of Object.entries(servers ?? {})) {
|
|
184
|
+
if (!/^[A-Za-z0-9._-]+$/.test(name)) {
|
|
185
|
+
throw new Error(`invalid MCP server name: ${name}`);
|
|
186
|
+
}
|
|
187
|
+
const transport = server.transport ?? (server.command ? 'stdio' : 'http');
|
|
188
|
+
if (transport === 'stdio') {
|
|
189
|
+
if (!server.command?.trim() || server.url !== undefined) {
|
|
190
|
+
throw new Error(`MCP server ${name} must set command and must not set url for stdio transport`);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
else if (!server.url?.trim() || server.command !== undefined) {
|
|
194
|
+
throw new Error(`MCP server ${name} must set url and must not set command for ${transport} transport`);
|
|
195
|
+
}
|
|
196
|
+
normalized[name] = {
|
|
197
|
+
transport,
|
|
198
|
+
...(server.command ? { command: server.command } : {}),
|
|
199
|
+
args: server.args ?? [],
|
|
200
|
+
...(server.cwd ? { cwd: server.cwd } : {}),
|
|
201
|
+
env: server.env ?? {},
|
|
202
|
+
...(server.url ? { url: server.url } : {}),
|
|
203
|
+
headers: server.headers ?? {},
|
|
204
|
+
enabled: server.enabled ?? true
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
return normalized;
|
|
208
|
+
}
|
|
172
209
|
function normalizeInstalledSkills(names) {
|
|
173
210
|
return [...new Set(names.map(normalizeSkillName))];
|
|
174
211
|
}
|