@technomoron/agent-run 0.99.29 → 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/dist/renderer.js CHANGED
@@ -4,8 +4,10 @@ exports.renderProfile = renderProfile;
4
4
  exports.syncAgentProfile = syncAgentProfile;
5
5
  exports.checkRenderedProfile = checkRenderedProfile;
6
6
  const fs = require("fs");
7
- const os = require("os");
8
7
  const path = require("path");
8
+ const registry_1 = require("./agents/registry");
9
+ const render_instructions_1 = require("./config/render-instructions");
10
+ const render_skills_1 = require("./config/render-skills");
9
11
  const constants_1 = require("./constants");
10
12
  const config_tree_1 = require("./config-tree");
11
13
  const defaults_1 = require("./defaults");
@@ -24,10 +26,32 @@ function renderProfile(projectRoot, agentDir, checkOnly = false, targetTool = nu
24
26
  (0, config_tree_1.ensureConfigRootGitignore)(configRoot);
25
27
  }
26
28
  const context = (0, templates_1.buildRenderContext)(projectRoot, agentDir, configRoot, manifest, env);
27
- context.renderedAgentSections = renderAgentSections(env, configRoot, manifest, context, trace);
28
- context.skills = renderSkills(env, configRoot, manifest, context, trace);
29
- const files = renderProfileFiles(env, configRoot, context, targetTool, trace);
30
- return { agentDir, configRoot, profile, context, files, skills: context.skills };
29
+ const canonicalInstructions = (0, render_instructions_1.buildCanonicalInstructions)(env, configRoot, manifest, context, trace);
30
+ context.renderedAgentSections = canonicalInstructions.sections;
31
+ context.skills = (0, render_skills_1.buildCanonicalSkills)(env, configRoot, manifest, context, trace);
32
+ const adapters = (0, registry_1.enabledAgentAdapters)(context).filter((adapter) => targetTool === null || adapter.id === targetTool);
33
+ const agentsMd = adapters.some((adapter) => adapter.id !== 'claude')
34
+ ? (0, render_instructions_1.writeAgentsMd)(env, configRoot, context, trace)
35
+ : '';
36
+ const claudeMd = adapters.some((adapter) => adapter.id === 'claude')
37
+ ? (0, render_instructions_1.writeClaudeMd)(env, configRoot, context, trace)
38
+ : '';
39
+ const runtimes = {};
40
+ for (const adapter of adapters) {
41
+ runtimes[adapter.id] = adapter.generate({
42
+ context,
43
+ configRoot,
44
+ env,
45
+ trace,
46
+ agentsMd,
47
+ claudeMd
48
+ });
49
+ }
50
+ const files = [
51
+ ...Object.values(runtimes).flatMap((runtime) => runtime?.files ?? []),
52
+ ...(0, guards_1.renderGuardShims)(context)
53
+ ];
54
+ return { agentDir, configRoot, profile, context, files, skills: context.skills, runtimes };
31
55
  }
32
56
  function validateManifestProfile(manifestProfile, inferredProfile, agentDir) {
33
57
  if (manifestProfile === inferredProfile) {
@@ -39,70 +63,9 @@ function validateManifestProfile(manifestProfile, inferredProfile, agentDir) {
39
63
  }
40
64
  throw new Error(`${path.join(agentDir, constants_1.MANIFEST_FILE_NAME)} profile must match inferred profile ${inferredProfile}`);
41
65
  }
42
- function renderAgentSections(env, configRoot, manifest, context, trace) {
43
- const sections = [(0, templates_1.renderTemplateFile)(env, configRoot, manifest.agent.base, context, trace)];
44
- for (const include of manifest.agent.includes) {
45
- const includePath = (0, templates_1.resolveConfigPath)(configRoot, include, context);
46
- if (!fs.existsSync(includePath)) {
47
- if (include.includes('AGENTS-MODS.md') || include.includes(constants_1.LOCAL_TEMPLATE_FILE_NAME)) {
48
- continue;
49
- }
50
- throw new Error(`missing template: ${includePath}`);
51
- }
52
- sections.push((0, templates_1.renderTemplateFile)(env, configRoot, include, context, trace));
53
- }
54
- const renderedSections = sections.map((section, index) => {
55
- const label = index === 0 ? manifest.agent.base : manifest.agent.includes[index - 1] ?? `section ${index}`;
56
- (0, templates_1.assertNoUnexpandedTemplateVars)(label, section);
57
- return section.trimEnd();
58
- });
59
- renderedSections.push(projectMemoryInstructions(context));
60
- return renderedSections;
61
- }
62
- function projectMemoryInstructions(context) {
63
- return [
64
- '## Project Memory',
65
- '',
66
- `Project memory is stored in ${context.paths.projectMemoryDir}.`,
67
- 'Read README.md there before starting work when prior project context may matter, then read only the linked files relevant to the task.',
68
- 'Do not store secrets, raw chat transcripts, or temporary task state there.',
69
- 'Update project memory only when the user explicitly asks you to remember or update something for this project.'
70
- ].join('\n');
71
- }
72
- function renderProfileFiles(env, configRoot, context, targetTool, trace) {
73
- const files = [];
74
- if (context.tools.codex && (targetTool === null || targetTool === 'codex')) {
75
- files.push({
76
- path: path.join(context.paths.codexHomeDir, 'AGENTS.md'),
77
- content: renderToolInstructions('AGENTS.md', env, configRoot, context, false, trace)
78
- });
79
- files.push({
80
- path: path.join(context.paths.codexHomeDir, 'config.toml'),
81
- content: renderCodexConfig(env, configRoot, context, trace)
82
- });
83
- files.push(...renderNativeSkillFiles(context, 'codex'));
84
- }
85
- if (context.tools.claude && (targetTool === null || targetTool === 'claude')) {
86
- files.push({
87
- path: path.join(context.paths.liveDir, 'CLAUDE.md'),
88
- content: renderToolInstructions('CLAUDE.md', env, configRoot, context, true, trace)
89
- });
90
- files.push({
91
- path: path.join(context.paths.liveDir, '.claude', 'agent-run-settings.json'),
92
- content: renderClaudeSettings(env, configRoot, context, trace)
93
- });
94
- files.push({
95
- path: path.join(context.paths.liveDir, '.claude', '.claude-plugin', 'plugin.json'),
96
- content: claudePluginManifestContent(context)
97
- });
98
- files.push(...renderNativeSkillFiles(context, 'claude'));
99
- }
100
- files.push(...(0, guards_1.renderGuardShims)(context));
101
- return files;
102
- }
103
66
  function syncAgentProfile(projectRoot, agentDir, options) {
104
67
  const rendered = renderProfile(projectRoot, agentDir, false, null, undefined, options);
105
- syncRuntimeDirs(rendered.context);
68
+ syncRuntimeDirs(rendered);
106
69
  removeStaleGeneratedEntries(rendered);
107
70
  for (const file of rendered.files) {
108
71
  writeGeneratedFile(file.path, file.content, file.executable ?? false);
@@ -111,6 +74,33 @@ function syncAgentProfile(projectRoot, agentDir, options) {
111
74
  removeLegacyGeneratedClaudeSettings(rendered.context);
112
75
  return rendered;
113
76
  }
77
+ function syncRuntimeDirs(rendered) {
78
+ const context = rendered.context;
79
+ const runtimeDirs = Object.values(rendered.runtimes).flatMap((runtime) => runtime?.requiredDirs ?? []);
80
+ for (const dir of [
81
+ context.paths.reviewDir,
82
+ context.paths.projectMemoryDir,
83
+ context.paths.memoriesDir,
84
+ context.paths.liveDir,
85
+ context.paths.binDir,
86
+ ...runtimeDirs
87
+ ]) {
88
+ fs.mkdirSync(dir, { recursive: true });
89
+ }
90
+ const projectMemoryIndex = path.join(context.paths.projectMemoryDir, 'README.md');
91
+ if (!fs.existsSync(projectMemoryIndex)) {
92
+ fs.writeFileSync(projectMemoryIndex, (0, defaults_1.defaultProjectMemoryIndex)(), 'utf8');
93
+ (0, utils_1.verbose)(`created ${projectMemoryIndex}`);
94
+ }
95
+ migrateLiveReviewFiles(context);
96
+ removeLegacyCodexSkillDirs(context);
97
+ removeLegacyCodeReviewSkillDirs(context);
98
+ for (const runtime of Object.values(rendered.runtimes)) {
99
+ if (runtime) {
100
+ (0, registry_1.getAgentAdapter)(runtime.id).prepare?.(runtime);
101
+ }
102
+ }
103
+ }
114
104
  function writeGeneratedFile(filePath, content, executable) {
115
105
  fs.mkdirSync(path.dirname(filePath), { recursive: true });
116
106
  const unchanged = fs.existsSync(filePath) && fs.readFileSync(filePath, 'utf8') === content;
@@ -128,24 +118,24 @@ function writeGeneratedFile(filePath, content, executable) {
128
118
  }
129
119
  function removeStaleGeneratedEntries(rendered) {
130
120
  const expectedFiles = new Set(rendered.files.map((file) => path.resolve(file.path)));
131
- const context = rendered.context;
132
- const generatedCandidates = [
133
- path.join(context.paths.codexHomeDir, 'AGENTS.md'),
134
- path.join(context.paths.codexHomeDir, 'config.toml'),
135
- path.join(context.paths.liveDir, 'CLAUDE.md'),
136
- path.join(context.paths.liveDir, '.claude', 'CLAUDE.md'),
137
- path.join(context.paths.liveDir, '.claude', 'agent-run-settings.json'),
138
- path.join(context.paths.liveDir, '.claude', '.claude-plugin', 'plugin.json'),
139
- ...['git', 'git.cmd', 'npm', 'npm.cmd', 'pnpm', 'pnpm.cmd', 'gh', 'gh.cmd'].map((name) => path.join(context.paths.binDir, name))
140
- ];
141
- for (const filePath of generatedCandidates) {
121
+ for (const adapter of (0, registry_1.listAgentAdapters)()) {
122
+ const agentLayout = adapter.layout(rendered.context);
123
+ for (const filePath of [agentLayout.instructionFile, ...agentLayout.configFiles]) {
124
+ if (!expectedFiles.has(path.resolve(filePath))) {
125
+ fs.rmSync(filePath, { force: true });
126
+ }
127
+ }
128
+ const expectedSkills = rendered.context.tools[adapter.id]
129
+ ? new Set(rendered.skills.map((skill) => skill.name))
130
+ : new Set();
131
+ removeStaleGeneratedSkills(agentLayout.skillsDir, expectedSkills, agentLayout.preservedSkillNames ?? new Set());
132
+ }
133
+ for (const name of ['git', 'git.cmd', 'npm', 'npm.cmd', 'pnpm', 'pnpm.cmd', 'gh', 'gh.cmd']) {
134
+ const filePath = path.join(rendered.context.paths.binDir, name);
142
135
  if (!expectedFiles.has(path.resolve(filePath))) {
143
136
  fs.rmSync(filePath, { force: true });
144
137
  }
145
138
  }
146
- const skillNames = new Set(rendered.skills.map((skill) => skill.name));
147
- removeStaleGeneratedSkills(context.paths.codexSkillsDir, context.tools.codex ? skillNames : new Set(), new Set(['.system']));
148
- removeStaleGeneratedSkills(context.paths.claudeSkillsDir, context.tools.claude ? skillNames : new Set());
149
139
  }
150
140
  function removeStaleGeneratedSkills(dir, expectedNames, preservedNames = new Set()) {
151
141
  if (!fs.existsSync(dir)) {
@@ -158,10 +148,7 @@ function removeStaleGeneratedSkills(dir, expectedNames, preservedNames = new Set
158
148
  }
159
149
  }
160
150
  function removeLegacyGeneratedCodexFiles(context) {
161
- for (const legacyPath of [
162
- path.join(context.paths.liveDir, 'AGENTS.md'),
163
- path.join(context.paths.liveDir, 'config.toml')
164
- ]) {
151
+ for (const legacyPath of [path.join(context.paths.liveDir, 'AGENTS.md'), path.join(context.paths.liveDir, 'config.toml')]) {
165
152
  if (fs.existsSync(legacyPath)) {
166
153
  fs.rmSync(legacyPath);
167
154
  (0, utils_1.verbose)(`removed legacy generated Codex file ${legacyPath}`);
@@ -193,9 +180,9 @@ function checkRenderedProfile(projectRoot, agentDir) {
193
180
  try {
194
181
  const rendered = renderProfile(projectRoot, agentDir, true);
195
182
  checkRenderedFiles(rendered, findings);
196
- checkRuntimeDirectories(rendered.context, findings);
183
+ checkRuntimeDirectories(rendered, findings);
197
184
  for (const skill of rendered.skills) {
198
- validateRenderedSkill(skill.renderedContent, skill.sourcePath);
185
+ (0, render_skills_1.validateRenderedSkill)(skill.renderedContent, skill.sourcePath);
199
186
  }
200
187
  }
201
188
  catch (error) {
@@ -219,16 +206,16 @@ function checkRenderedFiles(rendered, findings) {
219
206
  }
220
207
  }
221
208
  }
222
- function checkRuntimeDirectories(context, findings) {
209
+ function checkRuntimeDirectories(rendered, findings) {
210
+ const context = rendered.context;
211
+ const runtimeDirs = Object.values(rendered.runtimes).flatMap((runtime) => runtime?.requiredDirs ?? []);
223
212
  for (const dir of [
224
213
  context.paths.reviewDir,
225
214
  context.paths.projectMemoryDir,
226
215
  context.paths.memoriesDir,
227
- context.paths.codexHomeDir,
228
216
  context.paths.liveDir,
229
- context.paths.codexSkillsDir,
230
- context.paths.claudeSkillsDir,
231
- context.paths.binDir
217
+ context.paths.binDir,
218
+ ...runtimeDirs
232
219
  ]) {
233
220
  if (!fs.existsSync(dir)) {
234
221
  findings.push({ message: `missing generated runtime directory: ${dir}`, severity: 'ERROR' });
@@ -248,133 +235,6 @@ function checkConfigGitignore(configRoot, findings) {
248
235
  }
249
236
  }
250
237
  }
251
- function renderSkills(env, configRoot, manifest, context, trace) {
252
- return manifest.skills.install.map((name) => {
253
- const sourceTemplate = manifest.skills.overrides[name] ?? `global/skills/${name}/SKILL.md.njk`;
254
- const sourcePath = (0, templates_1.resolveConfigPath)(configRoot, sourceTemplate, context);
255
- if (!fs.existsSync(sourcePath)) {
256
- throw new Error(`missing skill template for ${name}: ${sourcePath}`);
257
- }
258
- const renderedContent = (0, templates_1.renderTemplateFile)(env, configRoot, sourceTemplate, context, trace);
259
- (0, templates_1.assertNoUnexpandedTemplateVars)(`skill ${name}`, renderedContent);
260
- validateRenderedSkill(renderedContent, sourcePath);
261
- return { name, sourcePath, renderedContent, description: extractSkillDescription(renderedContent) };
262
- });
263
- }
264
- function renderNativeSkillFiles(context, targetTool) {
265
- const targetDir = targetTool === 'codex' ? context.paths.codexSkillsDir : context.paths.claudeSkillsDir;
266
- return context.skills.map((skill) => ({
267
- path: path.join(targetDir, skill.name, 'SKILL.md'),
268
- content: skill.renderedContent
269
- }));
270
- }
271
- function renderToolInstructions(templateName, env, configRoot, context, isClaude, trace) {
272
- const templatePath = `global/tool-templates/${templateName}.njk`;
273
- const content = fs.existsSync(path.join(configRoot, templatePath))
274
- ? (0, templates_1.renderTemplateFile)(env, configRoot, templatePath, context, trace)
275
- : env.renderString((0, defaults_1.defaultToolInstructionsTemplate)(isClaude), context);
276
- (0, templates_1.assertNoUnexpandedTemplateVars)(templateName, content);
277
- return content.replace(/\n*$/, '\n');
278
- }
279
- function renderCodexConfig(env, configRoot, context, trace) {
280
- const templatePath = resolveProfileOverrideTemplate(configRoot, context, 'codex-config.toml.njk', 'global/tool-templates/codex-config.toml.njk');
281
- const content = templatePath
282
- ? (0, templates_1.renderTemplateFile)(env, configRoot, templatePath, context, trace)
283
- : (0, defaults_1.defaultCodexConfigContent)(context);
284
- (0, templates_1.assertNoUnexpandedTemplateVars)('config.toml', content);
285
- return content.replace(/\n*$/, '\n');
286
- }
287
- function renderClaudeSettings(env, configRoot, context, trace) {
288
- const templatePath = resolveProfileOverrideTemplate(configRoot, context, 'claude-settings.json.njk', 'global/tool-templates/claude-settings.json.njk');
289
- const content = templatePath
290
- ? (0, templates_1.renderTemplateFile)(env, configRoot, templatePath, context, trace)
291
- : (0, defaults_1.defaultClaudeSettingsContent)(context);
292
- (0, templates_1.assertNoUnexpandedTemplateVars)('claude settings.json', content);
293
- try {
294
- JSON.parse(content);
295
- }
296
- catch (error) {
297
- throw new Error(`invalid generated Claude settings JSON: ${(0, utils_1.formatError)(error)}`);
298
- }
299
- return content.replace(/\n*$/, '\n');
300
- }
301
- function claudePluginManifestContent(context) {
302
- return `${JSON.stringify({
303
- name: 'agent-run-profile',
304
- description: `Generated skills for agent-run profile ${context.profile}`,
305
- version: '1.0.0',
306
- author: { name: 'Technomoron' }
307
- }, null, 2)}\n`;
308
- }
309
- function resolveProfileOverrideTemplate(configRoot, context, overrideFileName, globalTemplatePath) {
310
- const overridePath = `${context.profile}/overrides/${overrideFileName}`;
311
- if (fs.existsSync(path.join(configRoot, overridePath))) {
312
- return overridePath;
313
- }
314
- return fs.existsSync(path.join(configRoot, globalTemplatePath)) ? globalTemplatePath : null;
315
- }
316
- function syncRuntimeDirs(context) {
317
- for (const dir of [
318
- context.paths.reviewDir,
319
- context.paths.projectMemoryDir,
320
- context.paths.memoriesDir,
321
- context.paths.codexHomeDir,
322
- context.paths.codexSkillsDir,
323
- context.paths.claudeSkillsDir,
324
- context.paths.binDir,
325
- path.join(context.paths.liveDir, '.claude')
326
- ]) {
327
- fs.mkdirSync(dir, { recursive: true });
328
- }
329
- const projectMemoryIndex = path.join(context.paths.projectMemoryDir, 'README.md');
330
- if (!fs.existsSync(projectMemoryIndex)) {
331
- fs.writeFileSync(projectMemoryIndex, (0, defaults_1.defaultProjectMemoryIndex)(), 'utf8');
332
- (0, utils_1.verbose)(`created ${projectMemoryIndex}`);
333
- }
334
- migrateLiveReviewFiles(context);
335
- removeLegacyCodexSkillDirs(context);
336
- removeLegacyCodeReviewSkillDirs(context);
337
- ensureSharedCodexAuth(context.paths.codexHomeDir);
338
- }
339
- function ensureSharedCodexAuth(codexHomeDir) {
340
- const sharedAuthPath = path.join(os.homedir(), '.codex', 'auth.json');
341
- if (!fs.existsSync(sharedAuthPath)) {
342
- return;
343
- }
344
- const profileAuthPath = path.join(codexHomeDir, 'auth.json');
345
- if (path.resolve(profileAuthPath) === path.resolve(sharedAuthPath) || isSymlinkTo(profileAuthPath, sharedAuthPath)) {
346
- return;
347
- }
348
- if (fs.existsSync(profileAuthPath) || (0, utils_1.isSymlink)(profileAuthPath)) {
349
- const backupPath = (0, utils_1.nextBackupPath)(profileAuthPath);
350
- fs.renameSync(profileAuthPath, backupPath);
351
- (0, utils_1.verbose)(`backed up profile Codex auth ${profileAuthPath} -> ${backupPath}`);
352
- }
353
- fs.mkdirSync(path.dirname(profileAuthPath), { recursive: true });
354
- try {
355
- fs.symlinkSync(sharedAuthPath, profileAuthPath);
356
- (0, utils_1.verbose)(`linked profile Codex auth ${profileAuthPath} -> ${sharedAuthPath}`);
357
- }
358
- catch (error) {
359
- if (!constants_1.IS_WINDOWS) {
360
- throw error;
361
- }
362
- fs.copyFileSync(sharedAuthPath, profileAuthPath);
363
- (0, utils_1.verbose)(`copied shared Codex auth ${sharedAuthPath} -> ${profileAuthPath}`);
364
- }
365
- }
366
- function isSymlinkTo(filePath, targetPath) {
367
- try {
368
- if (!fs.lstatSync(filePath).isSymbolicLink()) {
369
- return false;
370
- }
371
- const linkTarget = fs.readlinkSync(filePath);
372
- return path.resolve(path.dirname(filePath), linkTarget) === path.resolve(targetPath);
373
- }
374
- catch {
375
- return false;
376
- }
377
- }
378
238
  function removeLegacyCodexSkillDirs(context) {
379
239
  const legacyCodexDir = path.join(context.paths.liveDir, '.agents');
380
240
  if (!(0, utils_1.isSamePathOrDescendant)(context.paths.codexSkillsDir, legacyCodexDir)) {
@@ -387,8 +247,7 @@ function removeLegacyCodeReviewSkillDirs(context) {
387
247
  }
388
248
  for (const dir of [
389
249
  path.join(context.paths.liveDir, '.agents', 'skills', 'code-review'),
390
- path.join(context.paths.codexSkillsDir, 'code-review'),
391
- path.join(context.paths.claudeSkillsDir, 'code-review')
250
+ ...(0, registry_1.listAgentAdapters)().map((adapter) => path.join(adapter.layout(context).skillsDir, 'code-review'))
392
251
  ]) {
393
252
  fs.rmSync(dir, { recursive: true, force: true });
394
253
  }
@@ -441,27 +300,3 @@ function moveFile(sourcePath, targetPath) {
441
300
  fs.rmSync(sourcePath);
442
301
  }
443
302
  }
444
- function validateRenderedSkill(content, sourcePath) {
445
- const frontMatter = /^---\n([\s\S]*?)\n---\n/.exec(content.replace(/\r\n/g, '\n'));
446
- if (frontMatter === null) {
447
- throw new Error(`generated skill missing YAML front matter: ${sourcePath}`);
448
- }
449
- const yaml = frontMatter[1] ?? '';
450
- if (!/^name:\s*\S+/m.test(yaml)) {
451
- throw new Error(`generated skill missing name: ${sourcePath}`);
452
- }
453
- if (!/^description:\s*(?:\S|>\s*$)/m.test(yaml)) {
454
- throw new Error(`generated skill missing description: ${sourcePath}`);
455
- }
456
- }
457
- function extractSkillDescription(content) {
458
- const normalized = content.replace(/\r\n/g, '\n');
459
- const simple = /^description:\s*['"]?(.+?)['"]?\s*$/m.exec(normalized);
460
- if (simple?.[1]) {
461
- return simple[1].trim();
462
- }
463
- const folded = /^description:\s*>\s*\n((?:[ \t]+.+\n?)+)/m.exec(normalized);
464
- return folded?.[1]
465
- ? folded[1].split('\n').map((line) => line.trim()).filter(Boolean).join(' ')
466
- : '';
467
- }
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.buildAgentEnvironment = buildAgentEnvironment;
4
+ const path = require("path");
5
+ function buildAgentEnvironment(context) {
6
+ const realPath = process.env.AGENT_RUN_REAL_PATH ?? process.env.PATH ?? '';
7
+ return {
8
+ ...process.env,
9
+ AGENT_DIR: context.paths.liveDir,
10
+ AGENT_PROFILE_DIR: context.profileDir,
11
+ AGENT_RUN_PROJECT_ROOT: context.projectRoot,
12
+ AGENT_PROJECT_MEMORY_DIR: context.paths.projectMemoryDir,
13
+ AGENT_GLOBAL_MEMORY_DIR: context.paths.globalMemoryDir,
14
+ AGENT_RUN_REAL_PATH: realPath,
15
+ PATH: `${context.paths.binDir}${path.delimiter}${realPath}`
16
+ };
17
+ }
@@ -0,0 +1,40 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.postflightProjectCheck = postflightProjectCheck;
4
+ const fs = require("fs");
5
+ const path = require("path");
6
+ const project_1 = require("../project");
7
+ function postflightProjectCheck(context, allowLocal, code) {
8
+ if (!context.guardrails.forbidRepoAiFiles) {
9
+ return code;
10
+ }
11
+ if (!allowLocal) {
12
+ removeClaudeLocalSettings(context.projectRoot);
13
+ }
14
+ const localAiFiles = (0, project_1.findLocalAiFiles)(context.projectRoot, context.profileDir);
15
+ if (localAiFiles.length === 0) {
16
+ return code;
17
+ }
18
+ (0, project_1.warnForLocalAiFiles)(null, context.projectRoot, localAiFiles);
19
+ return allowLocal ? code : 1;
20
+ }
21
+ function removeClaudeLocalSettings(projectRoot) {
22
+ const claudeDir = path.join(projectRoot, '.claude');
23
+ const localSettingsPath = path.join(claudeDir, 'settings.local.json');
24
+ if (!fs.existsSync(localSettingsPath)) {
25
+ return;
26
+ }
27
+ fs.rmSync(localSettingsPath);
28
+ try {
29
+ fs.rmdirSync(claudeDir);
30
+ }
31
+ catch (error) {
32
+ if (!isDirectoryNotEmptyError(error)) {
33
+ throw error;
34
+ }
35
+ }
36
+ process.stderr.write(`agent-run: removed Claude's project-local settings: ${localSettingsPath}\n`);
37
+ }
38
+ function isDirectoryNotEmptyError(error) {
39
+ return error instanceof Error && 'code' in error && error.code === 'ENOTEMPTY';
40
+ }
@@ -0,0 +1,44 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.linkSharedEntry = linkSharedEntry;
4
+ const fs = require("fs");
5
+ const path = require("path");
6
+ const constants_1 = require("../constants");
7
+ const utils_1 = require("../utils");
8
+ function linkSharedEntry(sourcePath, targetPath, label) {
9
+ if (!fs.existsSync(sourcePath) && !(0, utils_1.isSymlink)(sourcePath)) {
10
+ return;
11
+ }
12
+ if (path.resolve(sourcePath) === path.resolve(targetPath) || isSymlinkTo(targetPath, sourcePath)) {
13
+ return;
14
+ }
15
+ if (fs.existsSync(targetPath) || (0, utils_1.isSymlink)(targetPath)) {
16
+ const backupPath = (0, utils_1.nextBackupPath)(targetPath);
17
+ fs.renameSync(targetPath, backupPath);
18
+ (0, utils_1.verbose)(`backed up ${label} ${targetPath} -> ${backupPath}`);
19
+ }
20
+ fs.mkdirSync(path.dirname(targetPath), { recursive: true });
21
+ try {
22
+ fs.symlinkSync(sourcePath, targetPath, fs.statSync(sourcePath).isDirectory() ? 'junction' : 'file');
23
+ (0, utils_1.verbose)(`linked ${label} ${targetPath} -> ${sourcePath}`);
24
+ }
25
+ catch (error) {
26
+ if (!constants_1.IS_WINDOWS || fs.statSync(sourcePath).isDirectory()) {
27
+ throw error;
28
+ }
29
+ fs.copyFileSync(sourcePath, targetPath);
30
+ (0, utils_1.verbose)(`copied ${label} ${sourcePath} -> ${targetPath}`);
31
+ }
32
+ }
33
+ function isSymlinkTo(filePath, targetPath) {
34
+ try {
35
+ if (!fs.lstatSync(filePath).isSymbolicLink()) {
36
+ return false;
37
+ }
38
+ const linkTarget = fs.readlinkSync(filePath);
39
+ return path.resolve(path.dirname(filePath), linkTarget) === path.resolve(targetPath);
40
+ }
41
+ catch {
42
+ return false;
43
+ }
44
+ }
@@ -0,0 +1,7 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.spawnAgent = spawnAgent;
4
+ const process_1 = require("../process");
5
+ function spawnAgent(spec) {
6
+ (0, process_1.execCommand)(spec.command, spec.args, spec.env, spec.cwd, spec.onExit);
7
+ }
package/dist/templates.js CHANGED
@@ -31,12 +31,16 @@ function buildRenderContext(projectRoot, profileDir, configRoot, manifest, env)
31
31
  date,
32
32
  checks: manifest.checks,
33
33
  tools: manifest.tools,
34
+ mcpServers: manifest.mcpServers,
34
35
  guardrails: manifest.guardrails
35
36
  };
36
37
  const reviewDir = resolveRuntimePath(configRoot, manifest.paths.reviewDir, env, baseContext);
37
38
  const memoriesDir = resolveRuntimePath(configRoot, manifest.paths.memoriesDir, env, baseContext);
38
39
  const projectMemoryDir = resolveRuntimePath(configRoot, manifest.paths.projectMemoryDir, env, baseContext);
39
40
  const codexHomeDir = path.join(memoriesDir, 'codex-home');
41
+ const geminiRuntimeDir = path.join(liveDir, 'gemini');
42
+ const geminiHomeDir = path.join(geminiRuntimeDir, 'home');
43
+ const grokRuntimeDir = path.join(liveDir, 'grok');
40
44
  const paths = {
41
45
  profileDir,
42
46
  liveDir,
@@ -54,6 +58,11 @@ function buildRenderContext(projectRoot, profileDir, configRoot, manifest, env)
54
58
  overridesDir: path.join(profileDir, 'overrides'),
55
59
  codexSkillsDir: path.join(codexHomeDir, 'skills'),
56
60
  claudeSkillsDir: path.join(liveDir, '.claude', 'skills'),
61
+ geminiRuntimeDir,
62
+ geminiHomeDir,
63
+ geminiSkillsDir: path.join(geminiHomeDir, '.agents', 'skills'),
64
+ grokRuntimeDir,
65
+ grokSkillsDir: path.join(grokRuntimeDir, 'skills'),
57
66
  binDir: path.join(liveDir, 'bin')
58
67
  };
59
68
  return {
@@ -17,7 +17,12 @@
17
17
  },
18
18
  "tools": {
19
19
  "codex": true,
20
- "claude": true
20
+ "claude": true,
21
+ "gemini": true,
22
+ "grok": true
23
+ },
24
+ "mcp": {
25
+ "servers": {}
21
26
  },
22
27
  "checks": [
23
28
  "agent-run check .",
@@ -0,0 +1,5 @@
1
+ {
2
+ "general": {
3
+ "enableAutoUpdate": true
4
+ }
5
+ }
@@ -0,0 +1 @@
1
+ # Generated by agent-run. Do not edit directly.
@@ -17,6 +17,26 @@
17
17
  "triage": "{{ profile }}/overrides/triage.SKILL.md.njk"
18
18
  }
19
19
  },
20
+ "mcp": {
21
+ "servers": {
22
+ "example-stdio": {
23
+ "command": "node",
24
+ "args": ["tools/example-mcp.js"],
25
+ "env": {
26
+ "EXAMPLE_TOKEN": "${EXAMPLE_TOKEN}"
27
+ },
28
+ "enabled": false
29
+ },
30
+ "example-http": {
31
+ "transport": "http",
32
+ "url": "https://example.invalid/mcp",
33
+ "headers": {
34
+ "Authorization": "Bearer ${EXAMPLE_TOKEN}"
35
+ },
36
+ "enabled": false
37
+ }
38
+ }
39
+ },
20
40
  "checks": [
21
41
  "agent-run check .",
22
42
  "pnpm test"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@technomoron/agent-run",
3
- "version": "0.99.29",
3
+ "version": "0.99.30",
4
4
  "packageManager": "pnpm@10.28.1",
5
5
  "bin": {
6
6
  "agent-run": "dist/agent-run.js"