@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/CHANGES +13 -0
- package/README.md +131 -20
- 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/cli.js +31 -7
- package/dist/commands.js +33 -9
- 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/constants.js +5 -2
- package/dist/defaults.js +31 -5
- package/dist/manifest.js +37 -1
- package/dist/renderer.js +80 -245
- 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 +9 -0
- package/examples/basic-config/agent-config/agent-run.defaults.jsonc +6 -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/package.json +1 -1
- package/scripts/update-ai-tools.sh +25 -49
- package/dist/tools.js +0 -125
package/dist/cli.js
CHANGED
|
@@ -4,6 +4,8 @@ exports.parseInvocation = parseInvocation;
|
|
|
4
4
|
const fs = require("fs");
|
|
5
5
|
const path = require("path");
|
|
6
6
|
const constants_1 = require("./constants");
|
|
7
|
+
const registry_1 = require("./agents/registry");
|
|
8
|
+
const types_1 = require("./agents/types");
|
|
7
9
|
const project_1 = require("./project");
|
|
8
10
|
const utils_1 = require("./utils");
|
|
9
11
|
function parseInvocation(invokedTool, argv) {
|
|
@@ -25,7 +27,7 @@ function applyGlobalOptions(options) {
|
|
|
25
27
|
}
|
|
26
28
|
function extractCommand(args) {
|
|
27
29
|
if (args.length === 0) {
|
|
28
|
-
(0, utils_1.fail)('usage: agent-run <codex|claude|check|setup|generate|edit|update> [options] (run with --help for details)');
|
|
30
|
+
(0, utils_1.fail)('usage: agent-run <codex|claude|gemini|grok|check|status|setup|generate|edit|update> [options] (run with --help for details)');
|
|
29
31
|
}
|
|
30
32
|
if (isHelpFlag(args[0])) {
|
|
31
33
|
printHelp('general');
|
|
@@ -48,6 +50,8 @@ function parseCommand(command, args) {
|
|
|
48
50
|
switch (command) {
|
|
49
51
|
case 'check':
|
|
50
52
|
return parseCheckCommand(args);
|
|
53
|
+
case 'status':
|
|
54
|
+
return parseStatusCommand(args);
|
|
51
55
|
case 'init':
|
|
52
56
|
return parseInitCommand(args);
|
|
53
57
|
case 'generate':
|
|
@@ -162,7 +166,11 @@ function applySandboxRunOption(command, arg, options) {
|
|
|
162
166
|
if (!['--yolo', '--sandboxed', '--network'].includes(arg)) {
|
|
163
167
|
return false;
|
|
164
168
|
}
|
|
165
|
-
|
|
169
|
+
const adapter = (0, registry_1.getAgentAdapter)(command);
|
|
170
|
+
if (arg === '--sandboxed' && !adapter.wrapperOptions.sandboxed) {
|
|
171
|
+
(0, utils_1.fail)(`${arg} is not supported for agent-run ${command}`);
|
|
172
|
+
}
|
|
173
|
+
if (arg === '--network' && !adapter.wrapperOptions.network) {
|
|
166
174
|
(0, utils_1.fail)(`${arg} is only supported for agent-run codex`);
|
|
167
175
|
}
|
|
168
176
|
if (arg === '--network') {
|
|
@@ -176,6 +184,15 @@ function applySandboxRunOption(command, arg, options) {
|
|
|
176
184
|
options.sandboxMode = mode;
|
|
177
185
|
return true;
|
|
178
186
|
}
|
|
187
|
+
function parseStatusCommand(inputArgs) {
|
|
188
|
+
for (const arg of inputArgs) {
|
|
189
|
+
if (isHelpFlag(arg)) {
|
|
190
|
+
printHelp('status');
|
|
191
|
+
}
|
|
192
|
+
(0, utils_1.fail)(`unknown status option: ${arg}`);
|
|
193
|
+
}
|
|
194
|
+
return { command: 'status' };
|
|
195
|
+
}
|
|
179
196
|
function parseCheckCommand(inputArgs) {
|
|
180
197
|
let all = false;
|
|
181
198
|
let targetPath = process.cwd();
|
|
@@ -298,6 +315,7 @@ function renderHelp(topic) {
|
|
|
298
315
|
' --all Check every repo under path',
|
|
299
316
|
''
|
|
300
317
|
],
|
|
318
|
+
status: ['Usage:', ' agent-run status', '', 'Show the native agent capability matrix.', ''],
|
|
301
319
|
init: ['Usage:', ' agent-run init [path]', '', 'Deprecated alias for `agent-run generate [path]`.', ''],
|
|
302
320
|
generate: ['Usage:', ' agent-run generate [path]', '', 'Create a minimal mapped profile marker and render generated files.', ''],
|
|
303
321
|
setup: ['Usage:', ' agent-run setup [org/repo]', '', 'Install the default config tree and selected profile.', ''],
|
|
@@ -328,15 +346,20 @@ function renderHelp(topic) {
|
|
|
328
346
|
`agent-run ${agentRunVersion()}`,
|
|
329
347
|
'',
|
|
330
348
|
'Usage:',
|
|
331
|
-
' agent-run <codex|claude|check|setup|generate|edit|update> [options]',
|
|
349
|
+
' agent-run <codex|claude|gemini|grok|check|status|setup|generate|edit|update> [options]',
|
|
332
350
|
'',
|
|
333
351
|
'Commands:',
|
|
334
352
|
' codex [--none] [--create] [--local] [--show] [--generate] [--yolo|--sandboxed] [--network] [args...]',
|
|
335
353
|
' Run codex with generated private config',
|
|
336
354
|
' claude [--none] [--create] [--local] [--show] [--generate] [--yolo] [args...]',
|
|
337
355
|
' Run claude with generated private config',
|
|
356
|
+
' gemini [--none] [--create] [--local] [--show] [--generate] [--yolo|--sandboxed] [args...]',
|
|
357
|
+
' Run gemini with generated private config',
|
|
358
|
+
' grok [--none] [--create] [--local] [--show] [--generate] [--yolo|--sandboxed] [args...]',
|
|
359
|
+
' Run grok with generated private config',
|
|
338
360
|
' setup [org/repo] Install the default tree and selected profile',
|
|
339
361
|
' check [--all] [path] Validate generated profile output',
|
|
362
|
+
' status Show native agent capabilities',
|
|
340
363
|
' generate [path] Create a sparse profile marker and render output',
|
|
341
364
|
' edit [path] Create or open local.md.njk',
|
|
342
365
|
' update [--all] [path] Regenerate existing profile output',
|
|
@@ -354,10 +377,11 @@ function renderHelp(topic) {
|
|
|
354
377
|
' --generate Generate files without running the tool',
|
|
355
378
|
' --yolo Run unattended without approval prompts:',
|
|
356
379
|
' Codex with -a never -s danger-full-access,',
|
|
357
|
-
' Claude with --dangerously-skip-permissions',
|
|
380
|
+
' Claude with --dangerously-skip-permissions,',
|
|
381
|
+
' Gemini with --yolo, Grok with --always-approve',
|
|
358
382
|
'',
|
|
359
|
-
'
|
|
360
|
-
' --sandboxed Run Codex
|
|
383
|
+
'Sandbox wrapper options:',
|
|
384
|
+
' --sandboxed Run Codex, Gemini, or Grok in its native workspace sandbox',
|
|
361
385
|
' --network Enable network for the workspace-write sandbox',
|
|
362
386
|
''
|
|
363
387
|
].join('\n');
|
|
@@ -379,7 +403,7 @@ function normalizeCommandName(value) {
|
|
|
379
403
|
if (base === 'agent-run') {
|
|
380
404
|
return null;
|
|
381
405
|
}
|
|
382
|
-
return [
|
|
406
|
+
return [...types_1.AGENT_IDS, 'check', 'status', 'setup', 'generate', 'init', 'edit', 'update', 'migrate-config'].includes(base)
|
|
383
407
|
? base
|
|
384
408
|
: null;
|
|
385
409
|
}
|
package/dist/commands.js
CHANGED
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.main = main;
|
|
4
4
|
const fs = require("fs");
|
|
5
5
|
const path = require("path");
|
|
6
|
+
const registry_1 = require("./agents/registry");
|
|
6
7
|
const checks_1 = require("./checks");
|
|
7
8
|
const cli_1 = require("./cli");
|
|
8
9
|
const config_tree_1 = require("./config-tree");
|
|
@@ -11,7 +12,7 @@ const manifest_1 = require("./manifest");
|
|
|
11
12
|
const process_1 = require("./process");
|
|
12
13
|
const project_1 = require("./project");
|
|
13
14
|
const renderer_1 = require("./renderer");
|
|
14
|
-
const
|
|
15
|
+
const spawn_agent_1 = require("./runtime/spawn-agent");
|
|
15
16
|
const utils_1 = require("./utils");
|
|
16
17
|
function main(invokedTool, argv) {
|
|
17
18
|
dispatch((0, cli_1.parseInvocation)(invokedTool, argv));
|
|
@@ -21,6 +22,9 @@ function dispatch(command) {
|
|
|
21
22
|
case 'check':
|
|
22
23
|
runCheck(command);
|
|
23
24
|
return;
|
|
25
|
+
case 'status':
|
|
26
|
+
runStatus(command);
|
|
27
|
+
return;
|
|
24
28
|
case 'init':
|
|
25
29
|
runGenerate(command);
|
|
26
30
|
return;
|
|
@@ -45,13 +49,18 @@ function dispatch(command) {
|
|
|
45
49
|
}
|
|
46
50
|
function runTool(parsed) {
|
|
47
51
|
const { args, command, wrapperArgs } = parsed;
|
|
52
|
+
const adapter = (0, registry_1.getAgentAdapter)(command);
|
|
48
53
|
const projectRoot = (0, project_1.findProjectRoot)(process.cwd());
|
|
49
54
|
(0, utils_1.verbose)(`run ${command}: cwd=${process.cwd()} projectRoot=${projectRoot}`);
|
|
50
55
|
validateRunModes(parsed, projectRoot);
|
|
51
56
|
if (wrapperArgs.none || (0, project_1.isIgnoredDir)(projectRoot)) {
|
|
52
57
|
(0, utils_1.verbose)(`wrapper bypassed for ${command}${wrapperArgs.none ? ' via --none' : ' because project is ignored'}`);
|
|
53
|
-
|
|
54
|
-
|
|
58
|
+
(0, spawn_agent_1.spawnAgent)({
|
|
59
|
+
command: (0, process_1.findRealBinary)(command),
|
|
60
|
+
args: [...adapter.bypassArgs(wrapperArgs), ...args],
|
|
61
|
+
cwd: process.cwd(),
|
|
62
|
+
env: { ...process.env }
|
|
63
|
+
});
|
|
55
64
|
return;
|
|
56
65
|
}
|
|
57
66
|
const profileResult = (0, project_1.resolveProfileResult)(projectRoot);
|
|
@@ -82,14 +91,29 @@ function runTool(parsed) {
|
|
|
82
91
|
}
|
|
83
92
|
checkLocalAiFiles(command, projectRoot, agentDir, preview.context, wrapperArgs.local);
|
|
84
93
|
const realBinary = (0, process_1.findRealBinary)(command);
|
|
85
|
-
const permissionArgs = (0, tools_1.getPermissionArgs)(command);
|
|
86
94
|
const rendered = (0, renderer_1.syncAgentProfile)(projectRoot, agentDir);
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
else {
|
|
91
|
-
(0, tools_1.runClaude)(realBinary, permissionArgs, rendered.context, args, wrapperArgs);
|
|
95
|
+
const runtime = rendered.runtimes[command];
|
|
96
|
+
if (!runtime) {
|
|
97
|
+
(0, utils_1.fail)(`no generated ${command} runtime found for profile ${rendered.profile}`);
|
|
92
98
|
}
|
|
99
|
+
(0, spawn_agent_1.spawnAgent)(adapter.spawn(runtime, { binary: realBinary, passthroughArgs: args, wrapperArgs }));
|
|
100
|
+
}
|
|
101
|
+
function runStatus(_parsed) {
|
|
102
|
+
const adapters = (0, registry_1.listAgentAdapters)();
|
|
103
|
+
const columns = adapters.map((adapter) => adapter.displayName);
|
|
104
|
+
const capabilityRows = [
|
|
105
|
+
['Instructions', 'instructions'],
|
|
106
|
+
['Skills', 'skills'],
|
|
107
|
+
['MCP', 'mcp'],
|
|
108
|
+
['Hooks', 'hooks'],
|
|
109
|
+
['Subagents', 'subagents'],
|
|
110
|
+
['Headless', 'headless']
|
|
111
|
+
];
|
|
112
|
+
const firstWidth = Math.max(...capabilityRows.map(([label]) => label.length));
|
|
113
|
+
const widths = columns.map((label) => Math.max(label.length, 3));
|
|
114
|
+
const header = `${''.padEnd(firstWidth)} ${columns.map((label, index) => label.padStart(widths[index] ?? 3)).join(' ')}`;
|
|
115
|
+
const rows = capabilityRows.map(([label, capability]) => `${label.padEnd(firstWidth)} ${adapters.map((adapter, index) => (adapter.capabilities[capability] ? 'yes' : '-').padStart(widths[index] ?? 3)).join(' ')}`);
|
|
116
|
+
process.stdout.write(['Native agent capabilities', '', header, ...rows, ''].join('\n'));
|
|
93
117
|
}
|
|
94
118
|
function validateRunModes(parsed, projectRoot) {
|
|
95
119
|
const { command, wrapperArgs } = parsed;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.buildCanonicalInstructions = buildCanonicalInstructions;
|
|
4
|
+
exports.writeAgentsMd = writeAgentsMd;
|
|
5
|
+
exports.writeClaudeMd = writeClaudeMd;
|
|
6
|
+
const fs = require("fs");
|
|
7
|
+
const path = require("path");
|
|
8
|
+
const constants_1 = require("../constants");
|
|
9
|
+
const defaults_1 = require("../defaults");
|
|
10
|
+
const templates_1 = require("../templates");
|
|
11
|
+
function buildCanonicalInstructions(env, configRoot, manifest, context, trace) {
|
|
12
|
+
const sections = [(0, templates_1.renderTemplateFile)(env, configRoot, manifest.agent.base, context, trace)];
|
|
13
|
+
for (const include of manifest.agent.includes) {
|
|
14
|
+
const includePath = (0, templates_1.resolveConfigPath)(configRoot, include, context);
|
|
15
|
+
if (!fs.existsSync(includePath)) {
|
|
16
|
+
if (include.includes('AGENTS-MODS.md') || include.includes(constants_1.LOCAL_TEMPLATE_FILE_NAME)) {
|
|
17
|
+
continue;
|
|
18
|
+
}
|
|
19
|
+
throw new Error(`missing template: ${includePath}`);
|
|
20
|
+
}
|
|
21
|
+
sections.push((0, templates_1.renderTemplateFile)(env, configRoot, include, context, trace));
|
|
22
|
+
}
|
|
23
|
+
const renderedSections = sections.map((section, index) => {
|
|
24
|
+
const label = index === 0 ? manifest.agent.base : manifest.agent.includes[index - 1] ?? `section ${index}`;
|
|
25
|
+
(0, templates_1.assertNoUnexpandedTemplateVars)(label, section);
|
|
26
|
+
return section.trimEnd();
|
|
27
|
+
});
|
|
28
|
+
renderedSections.push(projectMemoryInstructions(context));
|
|
29
|
+
return { sections: renderedSections };
|
|
30
|
+
}
|
|
31
|
+
function writeAgentsMd(env, configRoot, context, trace) {
|
|
32
|
+
return renderInstructionFile('AGENTS.md', env, configRoot, context, false, trace);
|
|
33
|
+
}
|
|
34
|
+
function writeClaudeMd(env, configRoot, context, trace) {
|
|
35
|
+
return renderInstructionFile('CLAUDE.md', env, configRoot, context, true, trace);
|
|
36
|
+
}
|
|
37
|
+
function renderInstructionFile(templateName, env, configRoot, context, isClaude, trace) {
|
|
38
|
+
const templatePath = `global/tool-templates/${templateName}.njk`;
|
|
39
|
+
const content = fs.existsSync(path.join(configRoot, templatePath))
|
|
40
|
+
? (0, templates_1.renderTemplateFile)(env, configRoot, templatePath, context, trace)
|
|
41
|
+
: env.renderString((0, defaults_1.defaultToolInstructionsTemplate)(isClaude), context);
|
|
42
|
+
(0, templates_1.assertNoUnexpandedTemplateVars)(templateName, content);
|
|
43
|
+
return content.replace(/\n*$/, '\n');
|
|
44
|
+
}
|
|
45
|
+
function projectMemoryInstructions(context) {
|
|
46
|
+
return [
|
|
47
|
+
'## Project Memory',
|
|
48
|
+
'',
|
|
49
|
+
`Project memory is stored in ${context.paths.projectMemoryDir}.`,
|
|
50
|
+
'Read README.md there before starting work when prior project context may matter, then read only the linked files relevant to the task.',
|
|
51
|
+
'Do not store secrets, raw chat transcripts, or temporary task state there.',
|
|
52
|
+
'Update project memory only when the user explicitly asks you to remember or update something for this project.'
|
|
53
|
+
].join('\n');
|
|
54
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.renderCodexMcp = renderCodexMcp;
|
|
4
|
+
exports.renderGrokMcp = renderGrokMcp;
|
|
5
|
+
exports.renderClaudeMcp = renderClaudeMcp;
|
|
6
|
+
exports.renderGeminiMcp = renderGeminiMcp;
|
|
7
|
+
function renderCodexMcp(context) {
|
|
8
|
+
return renderTomlMcp(context.mcpServers, 'codex');
|
|
9
|
+
}
|
|
10
|
+
function renderGrokMcp(context) {
|
|
11
|
+
return renderTomlMcp(context.mcpServers, 'grok');
|
|
12
|
+
}
|
|
13
|
+
function renderClaudeMcp(context) {
|
|
14
|
+
return { mcpServers: renderJsonMcp(context.mcpServers, 'claude') };
|
|
15
|
+
}
|
|
16
|
+
function renderGeminiMcp(context) {
|
|
17
|
+
return renderJsonMcp(context.mcpServers, 'gemini');
|
|
18
|
+
}
|
|
19
|
+
function renderJsonMcp(servers, target) {
|
|
20
|
+
const result = {};
|
|
21
|
+
for (const [name, server] of Object.entries(servers)) {
|
|
22
|
+
if (!server.enabled) {
|
|
23
|
+
continue;
|
|
24
|
+
}
|
|
25
|
+
if (server.transport === 'stdio') {
|
|
26
|
+
result[name] = compactObject({
|
|
27
|
+
...(target === 'claude' ? { type: 'stdio' } : {}),
|
|
28
|
+
command: server.command,
|
|
29
|
+
args: server.args.length > 0 ? server.args : undefined,
|
|
30
|
+
cwd: server.cwd,
|
|
31
|
+
env: Object.keys(server.env).length > 0 ? server.env : undefined
|
|
32
|
+
});
|
|
33
|
+
continue;
|
|
34
|
+
}
|
|
35
|
+
result[name] = compactObject({
|
|
36
|
+
...(target === 'claude' ? { type: server.transport } : {}),
|
|
37
|
+
...(target === 'gemini' && server.transport === 'http'
|
|
38
|
+
? { httpUrl: server.url }
|
|
39
|
+
: { url: server.url }),
|
|
40
|
+
headers: Object.keys(server.headers).length > 0 ? server.headers : undefined
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
return result;
|
|
44
|
+
}
|
|
45
|
+
function renderTomlMcp(servers, target) {
|
|
46
|
+
const sections = [];
|
|
47
|
+
for (const [name, server] of Object.entries(servers)) {
|
|
48
|
+
const lines = [`[mcp_servers.${tomlKey(name)}]`];
|
|
49
|
+
if (server.transport === 'stdio') {
|
|
50
|
+
lines.push(`command = ${tomlString(server.command ?? '')}`);
|
|
51
|
+
if (server.args.length > 0) {
|
|
52
|
+
lines.push(`args = ${tomlArray(server.args)}`);
|
|
53
|
+
}
|
|
54
|
+
if (server.cwd) {
|
|
55
|
+
lines.push(`cwd = ${tomlString(server.cwd)}`);
|
|
56
|
+
}
|
|
57
|
+
if (Object.keys(server.env).length > 0) {
|
|
58
|
+
lines.push(`env = ${tomlInlineTable(server.env)}`);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
lines.push(`url = ${tomlString(server.url ?? '')}`);
|
|
63
|
+
if (Object.keys(server.headers).length > 0) {
|
|
64
|
+
lines.push(`${target === 'codex' ? 'http_headers' : 'headers'} = ${tomlInlineTable(server.headers)}`);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
if (!server.enabled) {
|
|
68
|
+
lines.push('enabled = false');
|
|
69
|
+
}
|
|
70
|
+
sections.push(lines.join('\n'));
|
|
71
|
+
}
|
|
72
|
+
return sections.length > 0 ? `${sections.join('\n\n')}\n` : '';
|
|
73
|
+
}
|
|
74
|
+
function compactObject(value) {
|
|
75
|
+
return Object.fromEntries(Object.entries(value).filter(([, entry]) => entry !== undefined));
|
|
76
|
+
}
|
|
77
|
+
function tomlKey(value) {
|
|
78
|
+
return /^[A-Za-z0-9_-]+$/.test(value) ? value : tomlString(value);
|
|
79
|
+
}
|
|
80
|
+
function tomlString(value) {
|
|
81
|
+
return JSON.stringify(value);
|
|
82
|
+
}
|
|
83
|
+
function tomlArray(values) {
|
|
84
|
+
return `[${values.map(tomlString).join(', ')}]`;
|
|
85
|
+
}
|
|
86
|
+
function tomlInlineTable(values) {
|
|
87
|
+
return `{ ${Object.entries(values).map(([key, value]) => `${tomlKey(key)} = ${tomlString(value)}`).join(', ')} }`;
|
|
88
|
+
}
|
|
@@ -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/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
|
@@ -8,6 +8,8 @@ exports.defaultGlobalTemplates = defaultGlobalTemplates;
|
|
|
8
8
|
exports.defaultToolInstructionsTemplate = defaultToolInstructionsTemplate;
|
|
9
9
|
exports.defaultCodexConfigContent = defaultCodexConfigContent;
|
|
10
10
|
exports.defaultClaudeSettingsContent = defaultClaudeSettingsContent;
|
|
11
|
+
exports.defaultGeminiSettingsContent = defaultGeminiSettingsContent;
|
|
12
|
+
exports.defaultGrokConfigContent = defaultGrokConfigContent;
|
|
11
13
|
exports.legacyDefaultClaudeSettingsContent = legacyDefaultClaudeSettingsContent;
|
|
12
14
|
function defaultManifest(profile) {
|
|
13
15
|
return {
|
|
@@ -22,8 +24,11 @@ function defaultManifest(profile) {
|
|
|
22
24
|
},
|
|
23
25
|
tools: {
|
|
24
26
|
codex: true,
|
|
25
|
-
claude: true
|
|
27
|
+
claude: true,
|
|
28
|
+
gemini: true,
|
|
29
|
+
grok: true
|
|
26
30
|
},
|
|
31
|
+
mcp: { servers: {} },
|
|
27
32
|
checks: ['agent-run check .', 'pnpm run cleanbuild'],
|
|
28
33
|
guardrails: {
|
|
29
34
|
blockGitWrite: true,
|
|
@@ -95,6 +100,8 @@ function defaultGlobalTemplates() {
|
|
|
95
100
|
['global/snippets/verification.md.njk', defaultVerificationSnippet()],
|
|
96
101
|
['global/tool-templates/codex-config.toml.njk', defaultCodexConfigTemplate()],
|
|
97
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()],
|
|
98
105
|
['global/skills/commit-workflow/SKILL.md.njk', defaultCommitWorkflowSkill()],
|
|
99
106
|
['global/skills/github-release/SKILL.md.njk', defaultGithubReleaseSkill()],
|
|
100
107
|
['global/skills/release-package-check/SKILL.md.njk', defaultReleasePackageCheckSkill()],
|
|
@@ -138,7 +145,7 @@ function defaultNoAiFilesSnippet() {
|
|
|
138
145
|
return [
|
|
139
146
|
'## Agent File Storage',
|
|
140
147
|
'',
|
|
141
|
-
'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.',
|
|
142
149
|
'Generated agent-only files belong under {{ paths.liveDir }}.',
|
|
143
150
|
'Source config files belong under {{ profileDir }}.',
|
|
144
151
|
''
|
|
@@ -211,7 +218,7 @@ function defaultReleasePackageCheckSkill() {
|
|
|
211
218
|
'- The year in the top `CHANGES` version line appears in `package.json.copyright`.',
|
|
212
219
|
'- The git `origin` remote matches `<org>/<repo>`.',
|
|
213
220
|
'- The current branch has an upstream configured and is in sync with it after fetching.',
|
|
214
|
-
'- 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`.',
|
|
215
222
|
'- The repository root has a lockfile: `pnpm-lock.yaml`, `package-lock.json`, `yarn.lock`, `bun.lockb`, or `bun.lock`.',
|
|
216
223
|
'- Every GitHub Actions `uses:` reference is pinned to a full commit SHA.',
|
|
217
224
|
'- The pinned `actions/setup-node` release is v6-compatible and workflows use `node-version: 24`.',
|
|
@@ -342,11 +349,11 @@ function defaultToolInstructionsTemplate(isClaude) {
|
|
|
342
349
|
'',
|
|
343
350
|
isClaude
|
|
344
351
|
? 'Claude receives {{ agentDir }}/CLAUDE.md through `--append-system-prompt-file` and loads generated skills as a local plugin.'
|
|
345
|
-
: 'Codex
|
|
352
|
+
: 'Codex, Gemini, and Grok load this canonical AGENTS.md content from their private agent-run runtimes.',
|
|
346
353
|
'',
|
|
347
354
|
'## Mandatory Path Rule',
|
|
348
355
|
'',
|
|
349
|
-
'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.',
|
|
350
357
|
'Generated agent-only files must be stored under the agent directory shown above.',
|
|
351
358
|
'Source config files must be stored under the profile source directory shown above.',
|
|
352
359
|
''
|
|
@@ -394,6 +401,19 @@ function defaultClaudeSettingsTemplate() {
|
|
|
394
401
|
'{% endif %}'
|
|
395
402
|
].join('\n');
|
|
396
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
|
+
}
|
|
397
417
|
function defaultCodexConfigContent(context) {
|
|
398
418
|
return [
|
|
399
419
|
'# Generated by agent-run. Do not edit directly.',
|
|
@@ -428,6 +448,12 @@ function defaultClaudeSettingsContent(context) {
|
|
|
428
448
|
}
|
|
429
449
|
return `${JSON.stringify(settings, null, 2)}\n`;
|
|
430
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
|
+
}
|
|
431
457
|
function legacyDefaultClaudeSettingsContent(context, agentDir) {
|
|
432
458
|
return `${JSON.stringify({
|
|
433
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,
|
|
@@ -170,6 +178,34 @@ function normalizeManifest(manifest, profile) {
|
|
|
170
178
|
}
|
|
171
179
|
};
|
|
172
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
|
+
}
|
|
173
209
|
function normalizeInstalledSkills(names) {
|
|
174
210
|
return [...new Set(names.map(normalizeSkillName))];
|
|
175
211
|
}
|