@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
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
|
-
|
|
28
|
-
context.
|
|
29
|
-
|
|
30
|
-
|
|
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,58 +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
|
-
return 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
|
-
}
|
|
60
|
-
function renderProfileFiles(env, configRoot, context, targetTool, trace) {
|
|
61
|
-
const files = [];
|
|
62
|
-
if (context.tools.codex && (targetTool === null || targetTool === 'codex')) {
|
|
63
|
-
files.push({
|
|
64
|
-
path: path.join(context.paths.codexHomeDir, 'AGENTS.md'),
|
|
65
|
-
content: renderToolInstructions('AGENTS.md', env, configRoot, context, false, trace)
|
|
66
|
-
});
|
|
67
|
-
files.push({
|
|
68
|
-
path: path.join(context.paths.codexHomeDir, 'config.toml'),
|
|
69
|
-
content: renderCodexConfig(env, configRoot, context, trace)
|
|
70
|
-
});
|
|
71
|
-
files.push(...renderNativeSkillFiles(context, 'codex'));
|
|
72
|
-
}
|
|
73
|
-
if (context.tools.claude && (targetTool === null || targetTool === 'claude')) {
|
|
74
|
-
files.push({
|
|
75
|
-
path: path.join(context.paths.liveDir, 'CLAUDE.md'),
|
|
76
|
-
content: renderToolInstructions('CLAUDE.md', env, configRoot, context, true, trace)
|
|
77
|
-
});
|
|
78
|
-
files.push({
|
|
79
|
-
path: path.join(context.paths.liveDir, '.claude', 'agent-run-settings.json'),
|
|
80
|
-
content: renderClaudeSettings(env, configRoot, context, trace)
|
|
81
|
-
});
|
|
82
|
-
files.push({
|
|
83
|
-
path: path.join(context.paths.liveDir, '.claude', '.claude-plugin', 'plugin.json'),
|
|
84
|
-
content: claudePluginManifestContent(context)
|
|
85
|
-
});
|
|
86
|
-
files.push(...renderNativeSkillFiles(context, 'claude'));
|
|
87
|
-
}
|
|
88
|
-
files.push(...(0, guards_1.renderGuardShims)(context));
|
|
89
|
-
return files;
|
|
90
|
-
}
|
|
91
66
|
function syncAgentProfile(projectRoot, agentDir, options) {
|
|
92
67
|
const rendered = renderProfile(projectRoot, agentDir, false, null, undefined, options);
|
|
93
|
-
syncRuntimeDirs(rendered
|
|
68
|
+
syncRuntimeDirs(rendered);
|
|
94
69
|
removeStaleGeneratedEntries(rendered);
|
|
95
70
|
for (const file of rendered.files) {
|
|
96
71
|
writeGeneratedFile(file.path, file.content, file.executable ?? false);
|
|
@@ -99,6 +74,33 @@ function syncAgentProfile(projectRoot, agentDir, options) {
|
|
|
99
74
|
removeLegacyGeneratedClaudeSettings(rendered.context);
|
|
100
75
|
return rendered;
|
|
101
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
|
+
}
|
|
102
104
|
function writeGeneratedFile(filePath, content, executable) {
|
|
103
105
|
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
104
106
|
const unchanged = fs.existsSync(filePath) && fs.readFileSync(filePath, 'utf8') === content;
|
|
@@ -116,24 +118,24 @@ function writeGeneratedFile(filePath, content, executable) {
|
|
|
116
118
|
}
|
|
117
119
|
function removeStaleGeneratedEntries(rendered) {
|
|
118
120
|
const expectedFiles = new Set(rendered.files.map((file) => path.resolve(file.path)));
|
|
119
|
-
const
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
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);
|
|
130
135
|
if (!expectedFiles.has(path.resolve(filePath))) {
|
|
131
136
|
fs.rmSync(filePath, { force: true });
|
|
132
137
|
}
|
|
133
138
|
}
|
|
134
|
-
const skillNames = new Set(rendered.skills.map((skill) => skill.name));
|
|
135
|
-
removeStaleGeneratedSkills(context.paths.codexSkillsDir, context.tools.codex ? skillNames : new Set(), new Set(['.system']));
|
|
136
|
-
removeStaleGeneratedSkills(context.paths.claudeSkillsDir, context.tools.claude ? skillNames : new Set());
|
|
137
139
|
}
|
|
138
140
|
function removeStaleGeneratedSkills(dir, expectedNames, preservedNames = new Set()) {
|
|
139
141
|
if (!fs.existsSync(dir)) {
|
|
@@ -146,10 +148,7 @@ function removeStaleGeneratedSkills(dir, expectedNames, preservedNames = new Set
|
|
|
146
148
|
}
|
|
147
149
|
}
|
|
148
150
|
function removeLegacyGeneratedCodexFiles(context) {
|
|
149
|
-
for (const legacyPath of [
|
|
150
|
-
path.join(context.paths.liveDir, 'AGENTS.md'),
|
|
151
|
-
path.join(context.paths.liveDir, 'config.toml')
|
|
152
|
-
]) {
|
|
151
|
+
for (const legacyPath of [path.join(context.paths.liveDir, 'AGENTS.md'), path.join(context.paths.liveDir, 'config.toml')]) {
|
|
153
152
|
if (fs.existsSync(legacyPath)) {
|
|
154
153
|
fs.rmSync(legacyPath);
|
|
155
154
|
(0, utils_1.verbose)(`removed legacy generated Codex file ${legacyPath}`);
|
|
@@ -181,9 +180,9 @@ function checkRenderedProfile(projectRoot, agentDir) {
|
|
|
181
180
|
try {
|
|
182
181
|
const rendered = renderProfile(projectRoot, agentDir, true);
|
|
183
182
|
checkRenderedFiles(rendered, findings);
|
|
184
|
-
checkRuntimeDirectories(rendered
|
|
183
|
+
checkRuntimeDirectories(rendered, findings);
|
|
185
184
|
for (const skill of rendered.skills) {
|
|
186
|
-
validateRenderedSkill(skill.renderedContent, skill.sourcePath);
|
|
185
|
+
(0, render_skills_1.validateRenderedSkill)(skill.renderedContent, skill.sourcePath);
|
|
187
186
|
}
|
|
188
187
|
}
|
|
189
188
|
catch (error) {
|
|
@@ -207,15 +206,16 @@ function checkRenderedFiles(rendered, findings) {
|
|
|
207
206
|
}
|
|
208
207
|
}
|
|
209
208
|
}
|
|
210
|
-
function checkRuntimeDirectories(
|
|
209
|
+
function checkRuntimeDirectories(rendered, findings) {
|
|
210
|
+
const context = rendered.context;
|
|
211
|
+
const runtimeDirs = Object.values(rendered.runtimes).flatMap((runtime) => runtime?.requiredDirs ?? []);
|
|
211
212
|
for (const dir of [
|
|
212
213
|
context.paths.reviewDir,
|
|
214
|
+
context.paths.projectMemoryDir,
|
|
213
215
|
context.paths.memoriesDir,
|
|
214
|
-
context.paths.codexHomeDir,
|
|
215
216
|
context.paths.liveDir,
|
|
216
|
-
context.paths.
|
|
217
|
-
|
|
218
|
-
context.paths.binDir
|
|
217
|
+
context.paths.binDir,
|
|
218
|
+
...runtimeDirs
|
|
219
219
|
]) {
|
|
220
220
|
if (!fs.existsSync(dir)) {
|
|
221
221
|
findings.push({ message: `missing generated runtime directory: ${dir}`, severity: 'ERROR' });
|
|
@@ -235,127 +235,6 @@ function checkConfigGitignore(configRoot, findings) {
|
|
|
235
235
|
}
|
|
236
236
|
}
|
|
237
237
|
}
|
|
238
|
-
function renderSkills(env, configRoot, manifest, context, trace) {
|
|
239
|
-
return manifest.skills.install.map((name) => {
|
|
240
|
-
const sourceTemplate = manifest.skills.overrides[name] ?? `global/skills/${name}/SKILL.md.njk`;
|
|
241
|
-
const sourcePath = (0, templates_1.resolveConfigPath)(configRoot, sourceTemplate, context);
|
|
242
|
-
if (!fs.existsSync(sourcePath)) {
|
|
243
|
-
throw new Error(`missing skill template for ${name}: ${sourcePath}`);
|
|
244
|
-
}
|
|
245
|
-
const renderedContent = (0, templates_1.renderTemplateFile)(env, configRoot, sourceTemplate, context, trace);
|
|
246
|
-
(0, templates_1.assertNoUnexpandedTemplateVars)(`skill ${name}`, renderedContent);
|
|
247
|
-
validateRenderedSkill(renderedContent, sourcePath);
|
|
248
|
-
return { name, sourcePath, renderedContent, description: extractSkillDescription(renderedContent) };
|
|
249
|
-
});
|
|
250
|
-
}
|
|
251
|
-
function renderNativeSkillFiles(context, targetTool) {
|
|
252
|
-
const targetDir = targetTool === 'codex' ? context.paths.codexSkillsDir : context.paths.claudeSkillsDir;
|
|
253
|
-
return context.skills.map((skill) => ({
|
|
254
|
-
path: path.join(targetDir, skill.name, 'SKILL.md'),
|
|
255
|
-
content: skill.renderedContent
|
|
256
|
-
}));
|
|
257
|
-
}
|
|
258
|
-
function renderToolInstructions(templateName, env, configRoot, context, isClaude, trace) {
|
|
259
|
-
const templatePath = `global/tool-templates/${templateName}.njk`;
|
|
260
|
-
const content = fs.existsSync(path.join(configRoot, templatePath))
|
|
261
|
-
? (0, templates_1.renderTemplateFile)(env, configRoot, templatePath, context, trace)
|
|
262
|
-
: env.renderString((0, defaults_1.defaultToolInstructionsTemplate)(isClaude), context);
|
|
263
|
-
(0, templates_1.assertNoUnexpandedTemplateVars)(templateName, content);
|
|
264
|
-
return content.replace(/\n*$/, '\n');
|
|
265
|
-
}
|
|
266
|
-
function renderCodexConfig(env, configRoot, context, trace) {
|
|
267
|
-
const templatePath = resolveProfileOverrideTemplate(configRoot, context, 'codex-config.toml.njk', 'global/tool-templates/codex-config.toml.njk');
|
|
268
|
-
const content = templatePath
|
|
269
|
-
? (0, templates_1.renderTemplateFile)(env, configRoot, templatePath, context, trace)
|
|
270
|
-
: (0, defaults_1.defaultCodexConfigContent)(context);
|
|
271
|
-
(0, templates_1.assertNoUnexpandedTemplateVars)('config.toml', content);
|
|
272
|
-
return content.replace(/\n*$/, '\n');
|
|
273
|
-
}
|
|
274
|
-
function renderClaudeSettings(env, configRoot, context, trace) {
|
|
275
|
-
const templatePath = resolveProfileOverrideTemplate(configRoot, context, 'claude-settings.json.njk', 'global/tool-templates/claude-settings.json.njk');
|
|
276
|
-
const content = templatePath
|
|
277
|
-
? (0, templates_1.renderTemplateFile)(env, configRoot, templatePath, context, trace)
|
|
278
|
-
: (0, defaults_1.defaultClaudeSettingsContent)(context);
|
|
279
|
-
(0, templates_1.assertNoUnexpandedTemplateVars)('claude settings.json', content);
|
|
280
|
-
try {
|
|
281
|
-
JSON.parse(content);
|
|
282
|
-
}
|
|
283
|
-
catch (error) {
|
|
284
|
-
throw new Error(`invalid generated Claude settings JSON: ${(0, utils_1.formatError)(error)}`);
|
|
285
|
-
}
|
|
286
|
-
return content.replace(/\n*$/, '\n');
|
|
287
|
-
}
|
|
288
|
-
function claudePluginManifestContent(context) {
|
|
289
|
-
return `${JSON.stringify({
|
|
290
|
-
name: 'agent-run-profile',
|
|
291
|
-
description: `Generated skills for agent-run profile ${context.profile}`,
|
|
292
|
-
version: '1.0.0',
|
|
293
|
-
author: { name: 'Technomoron' }
|
|
294
|
-
}, null, 2)}\n`;
|
|
295
|
-
}
|
|
296
|
-
function resolveProfileOverrideTemplate(configRoot, context, overrideFileName, globalTemplatePath) {
|
|
297
|
-
const overridePath = `${context.profile}/overrides/${overrideFileName}`;
|
|
298
|
-
if (fs.existsSync(path.join(configRoot, overridePath))) {
|
|
299
|
-
return overridePath;
|
|
300
|
-
}
|
|
301
|
-
return fs.existsSync(path.join(configRoot, globalTemplatePath)) ? globalTemplatePath : null;
|
|
302
|
-
}
|
|
303
|
-
function syncRuntimeDirs(context) {
|
|
304
|
-
for (const dir of [
|
|
305
|
-
context.paths.reviewDir,
|
|
306
|
-
context.paths.memoriesDir,
|
|
307
|
-
context.paths.codexHomeDir,
|
|
308
|
-
context.paths.codexSkillsDir,
|
|
309
|
-
context.paths.claudeSkillsDir,
|
|
310
|
-
context.paths.binDir,
|
|
311
|
-
path.join(context.paths.liveDir, '.claude')
|
|
312
|
-
]) {
|
|
313
|
-
fs.mkdirSync(dir, { recursive: true });
|
|
314
|
-
}
|
|
315
|
-
migrateLiveReviewFiles(context);
|
|
316
|
-
removeLegacyCodexSkillDirs(context);
|
|
317
|
-
removeLegacyCodeReviewSkillDirs(context);
|
|
318
|
-
ensureSharedCodexAuth(context.paths.codexHomeDir);
|
|
319
|
-
}
|
|
320
|
-
function ensureSharedCodexAuth(codexHomeDir) {
|
|
321
|
-
const sharedAuthPath = path.join(os.homedir(), '.codex', 'auth.json');
|
|
322
|
-
if (!fs.existsSync(sharedAuthPath)) {
|
|
323
|
-
return;
|
|
324
|
-
}
|
|
325
|
-
const profileAuthPath = path.join(codexHomeDir, 'auth.json');
|
|
326
|
-
if (path.resolve(profileAuthPath) === path.resolve(sharedAuthPath) || isSymlinkTo(profileAuthPath, sharedAuthPath)) {
|
|
327
|
-
return;
|
|
328
|
-
}
|
|
329
|
-
if (fs.existsSync(profileAuthPath) || (0, utils_1.isSymlink)(profileAuthPath)) {
|
|
330
|
-
const backupPath = (0, utils_1.nextBackupPath)(profileAuthPath);
|
|
331
|
-
fs.renameSync(profileAuthPath, backupPath);
|
|
332
|
-
(0, utils_1.verbose)(`backed up profile Codex auth ${profileAuthPath} -> ${backupPath}`);
|
|
333
|
-
}
|
|
334
|
-
fs.mkdirSync(path.dirname(profileAuthPath), { recursive: true });
|
|
335
|
-
try {
|
|
336
|
-
fs.symlinkSync(sharedAuthPath, profileAuthPath);
|
|
337
|
-
(0, utils_1.verbose)(`linked profile Codex auth ${profileAuthPath} -> ${sharedAuthPath}`);
|
|
338
|
-
}
|
|
339
|
-
catch (error) {
|
|
340
|
-
if (!constants_1.IS_WINDOWS) {
|
|
341
|
-
throw error;
|
|
342
|
-
}
|
|
343
|
-
fs.copyFileSync(sharedAuthPath, profileAuthPath);
|
|
344
|
-
(0, utils_1.verbose)(`copied shared Codex auth ${sharedAuthPath} -> ${profileAuthPath}`);
|
|
345
|
-
}
|
|
346
|
-
}
|
|
347
|
-
function isSymlinkTo(filePath, targetPath) {
|
|
348
|
-
try {
|
|
349
|
-
if (!fs.lstatSync(filePath).isSymbolicLink()) {
|
|
350
|
-
return false;
|
|
351
|
-
}
|
|
352
|
-
const linkTarget = fs.readlinkSync(filePath);
|
|
353
|
-
return path.resolve(path.dirname(filePath), linkTarget) === path.resolve(targetPath);
|
|
354
|
-
}
|
|
355
|
-
catch {
|
|
356
|
-
return false;
|
|
357
|
-
}
|
|
358
|
-
}
|
|
359
238
|
function removeLegacyCodexSkillDirs(context) {
|
|
360
239
|
const legacyCodexDir = path.join(context.paths.liveDir, '.agents');
|
|
361
240
|
if (!(0, utils_1.isSamePathOrDescendant)(context.paths.codexSkillsDir, legacyCodexDir)) {
|
|
@@ -368,8 +247,7 @@ function removeLegacyCodeReviewSkillDirs(context) {
|
|
|
368
247
|
}
|
|
369
248
|
for (const dir of [
|
|
370
249
|
path.join(context.paths.liveDir, '.agents', 'skills', 'code-review'),
|
|
371
|
-
path.join(context.
|
|
372
|
-
path.join(context.paths.claudeSkillsDir, 'code-review')
|
|
250
|
+
...(0, registry_1.listAgentAdapters)().map((adapter) => path.join(adapter.layout(context).skillsDir, 'code-review'))
|
|
373
251
|
]) {
|
|
374
252
|
fs.rmSync(dir, { recursive: true, force: true });
|
|
375
253
|
}
|
|
@@ -422,27 +300,3 @@ function moveFile(sourcePath, targetPath) {
|
|
|
422
300
|
fs.rmSync(sourcePath);
|
|
423
301
|
}
|
|
424
302
|
}
|
|
425
|
-
function validateRenderedSkill(content, sourcePath) {
|
|
426
|
-
const frontMatter = /^---\n([\s\S]*?)\n---\n/.exec(content.replace(/\r\n/g, '\n'));
|
|
427
|
-
if (frontMatter === null) {
|
|
428
|
-
throw new Error(`generated skill missing YAML front matter: ${sourcePath}`);
|
|
429
|
-
}
|
|
430
|
-
const yaml = frontMatter[1] ?? '';
|
|
431
|
-
if (!/^name:\s*\S+/m.test(yaml)) {
|
|
432
|
-
throw new Error(`generated skill missing name: ${sourcePath}`);
|
|
433
|
-
}
|
|
434
|
-
if (!/^description:\s*(?:\S|>\s*$)/m.test(yaml)) {
|
|
435
|
-
throw new Error(`generated skill missing description: ${sourcePath}`);
|
|
436
|
-
}
|
|
437
|
-
}
|
|
438
|
-
function extractSkillDescription(content) {
|
|
439
|
-
const normalized = content.replace(/\r\n/g, '\n');
|
|
440
|
-
const simple = /^description:\s*['"]?(.+?)['"]?\s*$/m.exec(normalized);
|
|
441
|
-
if (simple?.[1]) {
|
|
442
|
-
return simple[1].trim();
|
|
443
|
-
}
|
|
444
|
-
const folded = /^description:\s*>\s*\n((?:[ \t]+.+\n?)+)/m.exec(normalized);
|
|
445
|
-
return folded?.[1]
|
|
446
|
-
? folded[1].split('\n').map((line) => line.trim()).filter(Boolean).join(' ')
|
|
447
|
-
: '';
|
|
448
|
-
}
|
|
@@ -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,11 +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);
|
|
39
|
+
const projectMemoryDir = resolveRuntimePath(configRoot, manifest.paths.projectMemoryDir, env, baseContext);
|
|
38
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');
|
|
39
44
|
const paths = {
|
|
40
45
|
profileDir,
|
|
41
46
|
liveDir,
|
|
@@ -47,10 +52,17 @@ function buildRenderContext(projectRoot, profileDir, configRoot, manifest, env)
|
|
|
47
52
|
: path.join(reviewDir, 'REVIEW.md'),
|
|
48
53
|
memoriesDir,
|
|
49
54
|
globalMemoryDir: path.join(configRoot, 'notes', 'memory'),
|
|
55
|
+
projectMemoryDir,
|
|
56
|
+
nativeMemoryDir: path.join(codexHomeDir, 'memories'),
|
|
50
57
|
codexHomeDir,
|
|
51
58
|
overridesDir: path.join(profileDir, 'overrides'),
|
|
52
59
|
codexSkillsDir: path.join(codexHomeDir, 'skills'),
|
|
53
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'),
|
|
54
66
|
binDir: path.join(liveDir, 'bin')
|
|
55
67
|
};
|
|
56
68
|
return {
|
|
@@ -9,7 +9,8 @@ It contains two pieces:
|
|
|
9
9
|
config root.
|
|
10
10
|
- `agent-config/`: a complete config root with shared manifest defaults, global
|
|
11
11
|
templates, snippets, skill templates, a personal memory skill, a sparse
|
|
12
|
-
profile manifest, profile-local instructions, and
|
|
12
|
+
profile manifest, durable project memory, profile-local instructions, and
|
|
13
|
+
profile overrides.
|
|
13
14
|
|
|
14
15
|
Try it from this repository after building:
|
|
15
16
|
|
|
@@ -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 .",
|
|
@@ -33,6 +38,7 @@
|
|
|
33
38
|
"changesFile": "{{ projectRoot }}/CHANGES",
|
|
34
39
|
"reviewDir": "{{ profileDir }}/reviews",
|
|
35
40
|
"reviewFile": "{{ profileDir }}/reviews/REVIEW-{{ date }}.md",
|
|
36
|
-
"memoriesDir": "{{ agentDir }}/memories"
|
|
41
|
+
"memoriesDir": "{{ agentDir }}/memories",
|
|
42
|
+
"projectMemoryDir": "{{ profileDir }}/notes/memory"
|
|
37
43
|
}
|
|
38
44
|
}
|
|
@@ -14,6 +14,8 @@ Do not edit this generated file. Edit root defaults or profile-specific source f
|
|
|
14
14
|
- reviewFile: `{{ paths.reviewFile }}`
|
|
15
15
|
- reviewConsolidatedFile: `{{ paths.reviewConsolidatedFile }}`
|
|
16
16
|
- memoriesDir: `{{ paths.memoriesDir }}`
|
|
17
|
+
- nativeMemoryDir: `{{ paths.nativeMemoryDir }}`
|
|
18
|
+
- projectMemoryDir: `{{ paths.projectMemoryDir }}`
|
|
17
19
|
- globalMemoryDir: `{{ paths.globalMemoryDir }}`
|
|
18
20
|
|
|
19
21
|
## Skills
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
"AGENT_DIR": {{ agentDir | dump }},
|
|
4
4
|
"AGENT_PROFILE_DIR": {{ profileDir | dump }},
|
|
5
5
|
"AGENT_RUN_PROJECT_ROOT": {{ projectRoot | dump }},
|
|
6
|
+
"AGENT_PROJECT_MEMORY_DIR": {{ paths.projectMemoryDir | dump }},
|
|
6
7
|
"AGENT_GLOBAL_MEMORY_DIR": {{ paths.globalMemoryDir | dump }},
|
|
7
8
|
"STARTER_PROFILE": {{ profile | dump }}
|
|
8
9
|
},
|
|
@@ -15,6 +16,7 @@
|
|
|
15
16
|
"AGENT_DIR": {{ agentDir | dump }},
|
|
16
17
|
"AGENT_PROFILE_DIR": {{ profileDir | dump }},
|
|
17
18
|
"AGENT_RUN_PROJECT_ROOT": {{ projectRoot | dump }},
|
|
19
|
+
"AGENT_PROJECT_MEMORY_DIR": {{ paths.projectMemoryDir | dump }},
|
|
18
20
|
"AGENT_GLOBAL_MEMORY_DIR": {{ paths.globalMemoryDir | dump }},
|
|
19
21
|
"STARTER_PROFILE": {{ profile | dump }}
|
|
20
22
|
}
|
|
@@ -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"
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
# Project memory
|
|
2
|
+
|
|
3
|
+
This directory contains durable notes for this project across agent sessions and machines.
|
|
4
|
+
Keep the notes short, factual, and free of secrets. Link additional topic files from this index.
|
|
5
|
+
|
|
6
|
+
## Files
|
|
7
|
+
|
|
8
|
+
- Add project memory files here as they are needed.
|