@technomoron/agent-run 0.99.28 → 0.99.30
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGES +26 -0
- package/README.md +202 -26
- package/dist/agents/claude.js +93 -0
- package/dist/agents/codex.js +77 -0
- package/dist/agents/gemini.js +112 -0
- package/dist/agents/grok.js +73 -0
- package/dist/agents/registry.js +26 -0
- package/dist/agents/shared.js +17 -0
- package/dist/agents/types.js +4 -0
- package/dist/checks.js +4 -0
- package/dist/cli.js +45 -12
- package/dist/commands.js +72 -18
- package/dist/config/render-instructions.js +54 -0
- package/dist/config/render-mcp.js +88 -0
- package/dist/config/render-settings.js +35 -0
- package/dist/config/render-skills.js +51 -0
- package/dist/config-tree.js +249 -0
- package/dist/constants.js +5 -2
- package/dist/defaults.js +65 -8
- package/dist/manifest.js +39 -2
- package/dist/renderer.js +81 -227
- package/dist/runtime/environment.js +17 -0
- package/dist/runtime/postflight.js +40 -0
- package/dist/runtime/shared-state.js +44 -0
- package/dist/runtime/spawn-agent.js +7 -0
- package/dist/templates.js +12 -0
- package/examples/basic-config/README.md +2 -1
- package/examples/basic-config/agent-config/agent-run.defaults.jsonc +8 -2
- package/examples/basic-config/agent-config/global/tool-templates/AGENTS.md.njk +2 -0
- package/examples/basic-config/agent-config/global/tool-templates/CLAUDE.md.njk +1 -0
- package/examples/basic-config/agent-config/global/tool-templates/claude-settings.json.njk +2 -0
- package/examples/basic-config/agent-config/global/tool-templates/codex-config.toml.njk +2 -1
- package/examples/basic-config/agent-config/global/tool-templates/gemini-settings.json.njk +5 -0
- package/examples/basic-config/agent-config/global/tool-templates/grok-config.toml.njk +1 -0
- package/examples/basic-config/agent-config/starter/basic-project/agent-run.jsonc +20 -0
- package/examples/basic-config/agent-config/starter/basic-project/notes/memory/README.md +8 -0
- package/examples/basic-config/agent-config/starter/basic-project/overrides/claude-settings.json.njk +2 -0
- package/examples/basic-config/agent-config/starter/basic-project/overrides/codex-config.toml.njk +2 -1
- package/ops/systemd/ai-tools-update.service +1 -1
- package/package.json +1 -1
- package/scripts/install-systemd-jobs.sh +15 -5
- package/scripts/update-ai-tools.sh +27 -16
- package/dist/tools.js +0 -119
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.grokAdapter = void 0;
|
|
4
|
+
const os = require("os");
|
|
5
|
+
const path = require("path");
|
|
6
|
+
const render_mcp_1 = require("../config/render-mcp");
|
|
7
|
+
const render_settings_1 = require("../config/render-settings");
|
|
8
|
+
const render_skills_1 = require("../config/render-skills");
|
|
9
|
+
const defaults_1 = require("../defaults");
|
|
10
|
+
const environment_1 = require("../runtime/environment");
|
|
11
|
+
const shared_state_1 = require("../runtime/shared-state");
|
|
12
|
+
const shared_1 = require("./shared");
|
|
13
|
+
function layout(context) {
|
|
14
|
+
return {
|
|
15
|
+
dir: context.paths.grokRuntimeDir,
|
|
16
|
+
instructionFile: path.join(context.paths.grokRuntimeDir, 'AGENTS.md'),
|
|
17
|
+
configFiles: [path.join(context.paths.grokRuntimeDir, 'config.toml')],
|
|
18
|
+
skillsDir: context.paths.grokSkillsDir,
|
|
19
|
+
requiredDirs: [context.paths.grokRuntimeDir, context.paths.grokSkillsDir]
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
function generate(generation) {
|
|
23
|
+
const agentLayout = layout(generation.context);
|
|
24
|
+
const baseConfig = (0, render_settings_1.renderProfileConfig)(generation.env, generation.configRoot, generation.context, 'grok-config.toml.njk', 'global/tool-templates/grok-config.toml.njk', () => (0, defaults_1.defaultGrokConfigContent)(), 'Grok config.toml', generation.trace);
|
|
25
|
+
const mcp = (0, render_mcp_1.renderGrokMcp)(generation.context);
|
|
26
|
+
const files = [
|
|
27
|
+
{ path: agentLayout.instructionFile, content: generation.agentsMd },
|
|
28
|
+
{ path: agentLayout.configFiles[0] ?? '', content: `${baseConfig.trimEnd()}${mcp ? `\n\n${mcp}` : '\n'}` },
|
|
29
|
+
...(0, render_skills_1.renderSkillFiles)(generation.context, agentLayout.skillsDir)
|
|
30
|
+
];
|
|
31
|
+
return { ...agentLayout, id: 'grok', projectDir: generation.context.projectRoot, files, context: generation.context };
|
|
32
|
+
}
|
|
33
|
+
exports.grokAdapter = {
|
|
34
|
+
id: 'grok',
|
|
35
|
+
displayName: 'Grok',
|
|
36
|
+
capabilities: {
|
|
37
|
+
instructions: true,
|
|
38
|
+
skills: true,
|
|
39
|
+
mcp: true,
|
|
40
|
+
hooks: true,
|
|
41
|
+
subagents: true,
|
|
42
|
+
headless: true
|
|
43
|
+
},
|
|
44
|
+
wrapperOptions: { sandboxed: true, network: false },
|
|
45
|
+
layout,
|
|
46
|
+
generate,
|
|
47
|
+
prepare(runtime) {
|
|
48
|
+
for (const name of ['auth.json', 'mcp_credentials.json']) {
|
|
49
|
+
(0, shared_state_1.linkSharedEntry)(path.join(os.homedir(), '.grok', name), path.join(runtime.dir, name), `Grok ${name}`);
|
|
50
|
+
}
|
|
51
|
+
},
|
|
52
|
+
spawn(runtime, options) {
|
|
53
|
+
(0, shared_1.assertRuntimeFile)('grok', runtime.instructionFile, runtime.context.profileDir);
|
|
54
|
+
const modeArgs = options.wrapperArgs.sandboxMode === 'danger'
|
|
55
|
+
? ['--always-approve']
|
|
56
|
+
: options.wrapperArgs.sandboxMode === 'sandboxed'
|
|
57
|
+
? ['--sandbox', 'workspace']
|
|
58
|
+
: [];
|
|
59
|
+
return {
|
|
60
|
+
command: options.binary,
|
|
61
|
+
args: [...modeArgs, ...options.passthroughArgs],
|
|
62
|
+
cwd: runtime.projectDir,
|
|
63
|
+
env: { ...(0, environment_1.buildAgentEnvironment)(runtime.context), GROK_HOME: runtime.dir },
|
|
64
|
+
onExit: (0, shared_1.withPostflight)(runtime, options.wrapperArgs)
|
|
65
|
+
};
|
|
66
|
+
},
|
|
67
|
+
bypassArgs(wrapperArgs) {
|
|
68
|
+
if (wrapperArgs.sandboxMode === 'danger') {
|
|
69
|
+
return ['--always-approve'];
|
|
70
|
+
}
|
|
71
|
+
return wrapperArgs.sandboxMode === 'sandboxed' ? ['--sandbox', 'workspace'] : [];
|
|
72
|
+
}
|
|
73
|
+
};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.agentRegistry = void 0;
|
|
4
|
+
exports.getAgentAdapter = getAgentAdapter;
|
|
5
|
+
exports.listAgentAdapters = listAgentAdapters;
|
|
6
|
+
exports.enabledAgentAdapters = enabledAgentAdapters;
|
|
7
|
+
const claude_1 = require("./claude");
|
|
8
|
+
const codex_1 = require("./codex");
|
|
9
|
+
const gemini_1 = require("./gemini");
|
|
10
|
+
const grok_1 = require("./grok");
|
|
11
|
+
const types_1 = require("./types");
|
|
12
|
+
exports.agentRegistry = {
|
|
13
|
+
codex: codex_1.codexAdapter,
|
|
14
|
+
claude: claude_1.claudeAdapter,
|
|
15
|
+
gemini: gemini_1.geminiAdapter,
|
|
16
|
+
grok: grok_1.grokAdapter
|
|
17
|
+
};
|
|
18
|
+
function getAgentAdapter(id) {
|
|
19
|
+
return exports.agentRegistry[id];
|
|
20
|
+
}
|
|
21
|
+
function listAgentAdapters() {
|
|
22
|
+
return types_1.AGENT_IDS.map((id) => exports.agentRegistry[id]);
|
|
23
|
+
}
|
|
24
|
+
function enabledAgentAdapters(context) {
|
|
25
|
+
return listAgentAdapters().filter((adapter) => context.tools[adapter.id]);
|
|
26
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.assertRuntimeFile = assertRuntimeFile;
|
|
4
|
+
exports.withPostflight = withPostflight;
|
|
5
|
+
const fs = require("fs");
|
|
6
|
+
const postflight_1 = require("../runtime/postflight");
|
|
7
|
+
function assertRuntimeFile(tool, filePath, profileDir) {
|
|
8
|
+
if (fs.existsSync(filePath)) {
|
|
9
|
+
return;
|
|
10
|
+
}
|
|
11
|
+
process.stderr.write(`agent-run: no ${tool} config found for this project: ${profileDir}\n`);
|
|
12
|
+
process.stderr.write(`agent-run: run \`agent-run ${tool} --none\` to bypass agent setup, or \`agent-run ${tool} --create\` to create profile files.\n`);
|
|
13
|
+
process.exit(1);
|
|
14
|
+
}
|
|
15
|
+
function withPostflight(runtime, wrapperArgs) {
|
|
16
|
+
return (code) => (0, postflight_1.postflightProjectCheck)(runtime.context, wrapperArgs.local, code);
|
|
17
|
+
}
|
package/dist/checks.js
CHANGED
|
@@ -8,6 +8,7 @@ exports.hasErrors = hasErrors;
|
|
|
8
8
|
const fs = require("fs");
|
|
9
9
|
const path = require("path");
|
|
10
10
|
const constants_1 = require("./constants");
|
|
11
|
+
const config_tree_1 = require("./config-tree");
|
|
11
12
|
const manifest_1 = require("./manifest");
|
|
12
13
|
const project_1 = require("./project");
|
|
13
14
|
const renderer_1 = require("./renderer");
|
|
@@ -47,6 +48,9 @@ function checkAgentDirectory(projectRoot, agentDir) {
|
|
|
47
48
|
if (!(0, manifest_1.isProfileConfigured)(agentDir)) {
|
|
48
49
|
return [{ message: `missing manifest or legacy/local source file in ${agentDir}`, severity: 'ERROR' }];
|
|
49
50
|
}
|
|
51
|
+
for (const legacyPath of (0, config_tree_1.describeLegacyProfileLayout)(agentDir)) {
|
|
52
|
+
findings.push({ message: `old profile layout needs migration: ${legacyPath}`, severity: 'ERROR' });
|
|
53
|
+
}
|
|
50
54
|
for (const relativeTemplate of constants_1.REQUIRED_GLOBAL_TEMPLATES) {
|
|
51
55
|
if (!fs.existsSync(path.join(configRoot, relativeTemplate))) {
|
|
52
56
|
findings.push({
|
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();
|
|
@@ -254,16 +271,22 @@ function parseUpdateCommand(inputArgs) {
|
|
|
254
271
|
}
|
|
255
272
|
function parseMigrateConfigCommand(inputArgs) {
|
|
256
273
|
let configRoot = (0, project_1.defaultConfigRoot)();
|
|
274
|
+
let yes = false;
|
|
257
275
|
for (const arg of inputArgs) {
|
|
258
276
|
if (isHelpFlag(arg)) {
|
|
259
277
|
printHelp('migrate-config');
|
|
260
278
|
}
|
|
261
|
-
if (arg
|
|
279
|
+
if (arg === '--yes') {
|
|
280
|
+
yes = true;
|
|
281
|
+
}
|
|
282
|
+
else if (arg.startsWith('--')) {
|
|
262
283
|
(0, utils_1.fail)(`unknown migrate-config option: ${arg}`);
|
|
263
284
|
}
|
|
264
|
-
|
|
285
|
+
else {
|
|
286
|
+
configRoot = arg;
|
|
287
|
+
}
|
|
265
288
|
}
|
|
266
|
-
return { command: 'migrate-config', configRoot: path.resolve(configRoot) };
|
|
289
|
+
return { command: 'migrate-config', configRoot: path.resolve(configRoot), yes };
|
|
267
290
|
}
|
|
268
291
|
function isHelpFlag(value) {
|
|
269
292
|
return value === '-h' || value === '--help';
|
|
@@ -292,6 +315,7 @@ function renderHelp(topic) {
|
|
|
292
315
|
' --all Check every repo under path',
|
|
293
316
|
''
|
|
294
317
|
],
|
|
318
|
+
status: ['Usage:', ' agent-run status', '', 'Show the native agent capability matrix.', ''],
|
|
295
319
|
init: ['Usage:', ' agent-run init [path]', '', 'Deprecated alias for `agent-run generate [path]`.', ''],
|
|
296
320
|
generate: ['Usage:', ' agent-run generate [path]', '', 'Create a minimal mapped profile marker and render generated files.', ''],
|
|
297
321
|
setup: ['Usage:', ' agent-run setup [org/repo]', '', 'Install the default config tree and selected profile.', ''],
|
|
@@ -306,9 +330,12 @@ function renderHelp(topic) {
|
|
|
306
330
|
],
|
|
307
331
|
'migrate-config': [
|
|
308
332
|
'Usage:',
|
|
309
|
-
' agent-run migrate-config [config-root]',
|
|
333
|
+
' agent-run migrate-config [--yes] [config-root]',
|
|
310
334
|
'',
|
|
311
335
|
'Convert an existing agent config tree to the current manifest and global-template layout.',
|
|
336
|
+
'',
|
|
337
|
+
'Options:',
|
|
338
|
+
' --yes Confirm tracked Git moves without prompting',
|
|
312
339
|
''
|
|
313
340
|
]
|
|
314
341
|
};
|
|
@@ -319,19 +346,24 @@ function renderHelp(topic) {
|
|
|
319
346
|
`agent-run ${agentRunVersion()}`,
|
|
320
347
|
'',
|
|
321
348
|
'Usage:',
|
|
322
|
-
' agent-run <codex|claude|check|setup|generate|edit|update> [options]',
|
|
349
|
+
' agent-run <codex|claude|gemini|grok|check|status|setup|generate|edit|update> [options]',
|
|
323
350
|
'',
|
|
324
351
|
'Commands:',
|
|
325
352
|
' codex [--none] [--create] [--local] [--show] [--generate] [--yolo|--sandboxed] [--network] [args...]',
|
|
326
353
|
' Run codex with generated private config',
|
|
327
354
|
' claude [--none] [--create] [--local] [--show] [--generate] [--yolo] [args...]',
|
|
328
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',
|
|
329
360
|
' setup [org/repo] Install the default tree and selected profile',
|
|
330
361
|
' check [--all] [path] Validate generated profile output',
|
|
362
|
+
' status Show native agent capabilities',
|
|
331
363
|
' generate [path] Create a sparse profile marker and render output',
|
|
332
364
|
' edit [path] Create or open local.md.njk',
|
|
333
365
|
' update [--all] [path] Regenerate existing profile output',
|
|
334
|
-
' migrate-config [config-root]
|
|
366
|
+
' migrate-config [--yes] [config-root] Convert existing config tree layout',
|
|
335
367
|
'',
|
|
336
368
|
'Global options:',
|
|
337
369
|
' -h, --help Show this help text',
|
|
@@ -345,10 +377,11 @@ function renderHelp(topic) {
|
|
|
345
377
|
' --generate Generate files without running the tool',
|
|
346
378
|
' --yolo Run unattended without approval prompts:',
|
|
347
379
|
' Codex with -a never -s danger-full-access,',
|
|
348
|
-
' Claude with --dangerously-skip-permissions',
|
|
380
|
+
' Claude with --dangerously-skip-permissions,',
|
|
381
|
+
' Gemini with --yolo, Grok with --always-approve',
|
|
349
382
|
'',
|
|
350
|
-
'
|
|
351
|
-
' --sandboxed Run Codex
|
|
383
|
+
'Sandbox wrapper options:',
|
|
384
|
+
' --sandboxed Run Codex, Gemini, or Grok in its native workspace sandbox',
|
|
352
385
|
' --network Enable network for the workspace-write sandbox',
|
|
353
386
|
''
|
|
354
387
|
].join('\n');
|
|
@@ -370,7 +403,7 @@ function normalizeCommandName(value) {
|
|
|
370
403
|
if (base === 'agent-run') {
|
|
371
404
|
return null;
|
|
372
405
|
}
|
|
373
|
-
return [
|
|
406
|
+
return [...types_1.AGENT_IDS, 'check', 'status', 'setup', 'generate', 'init', 'edit', 'update', 'migrate-config'].includes(base)
|
|
374
407
|
? base
|
|
375
408
|
: null;
|
|
376
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);
|
|
@@ -59,9 +68,9 @@ function runTool(parsed) {
|
|
|
59
68
|
(0, utils_1.fail)(profileResult.reason);
|
|
60
69
|
}
|
|
61
70
|
const profile = profileResult.profile;
|
|
71
|
+
const configRoot = (0, project_1.defaultConfigRoot)(projectRoot);
|
|
72
|
+
const agentDir = path.join(configRoot, profile);
|
|
62
73
|
if (wrapperArgs.show) {
|
|
63
|
-
const configRoot = (0, project_1.defaultConfigRoot)(projectRoot);
|
|
64
|
-
const agentDir = path.join(configRoot, profile);
|
|
65
74
|
(0, utils_1.verbose)(`profile=${profile} configRoot=${configRoot} agentPath=${agentDir}`);
|
|
66
75
|
requireProfile(command, agentDir);
|
|
67
76
|
showToolProfile(command, projectRoot, agentDir);
|
|
@@ -70,11 +79,10 @@ function runTool(parsed) {
|
|
|
70
79
|
if (wrapperArgs.create) {
|
|
71
80
|
runGenerate({ command: 'generate', targetPath: projectRoot });
|
|
72
81
|
}
|
|
73
|
-
const configRoot = (0, project_1.defaultConfigRoot)(projectRoot);
|
|
74
|
-
const agentDir = path.join(configRoot, profile);
|
|
75
82
|
(0, utils_1.verbose)(`profile=${profile} configRoot=${configRoot} agentPath=${agentDir}`);
|
|
76
83
|
prepareConfigRoot(configRoot);
|
|
77
84
|
requireProfile(command, agentDir);
|
|
85
|
+
migrateProfileOnStart(configRoot, agentDir, false);
|
|
78
86
|
const preview = (0, renderer_1.renderProfile)(projectRoot, agentDir, true, command);
|
|
79
87
|
ensureToolEnabled(preview.context, command);
|
|
80
88
|
if (wrapperArgs.generate) {
|
|
@@ -83,14 +91,29 @@ function runTool(parsed) {
|
|
|
83
91
|
}
|
|
84
92
|
checkLocalAiFiles(command, projectRoot, agentDir, preview.context, wrapperArgs.local);
|
|
85
93
|
const realBinary = (0, process_1.findRealBinary)(command);
|
|
86
|
-
const permissionArgs = (0, tools_1.getPermissionArgs)(command);
|
|
87
94
|
const rendered = (0, renderer_1.syncAgentProfile)(projectRoot, agentDir);
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
else {
|
|
92
|
-
(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}`);
|
|
93
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'));
|
|
94
117
|
}
|
|
95
118
|
function validateRunModes(parsed, projectRoot) {
|
|
96
119
|
const { command, wrapperArgs } = parsed;
|
|
@@ -158,6 +181,7 @@ function runGenerate(parsed) {
|
|
|
158
181
|
return;
|
|
159
182
|
}
|
|
160
183
|
const agentDir = initializeProfileSource(projectRoot, true);
|
|
184
|
+
migrateProfileOnStart((0, project_1.defaultConfigRoot)(projectRoot), agentDir, false);
|
|
161
185
|
printUpdateSummary((0, renderer_1.syncAgentProfile)(projectRoot, agentDir));
|
|
162
186
|
}
|
|
163
187
|
function runSetup(parsed) {
|
|
@@ -170,7 +194,9 @@ function runSetup(parsed) {
|
|
|
170
194
|
(0, utils_1.fail)(`starter config skeleton not found: ${sourcePath}`);
|
|
171
195
|
}
|
|
172
196
|
(0, config_tree_1.copySkeletonTree)(sourcePath, configRoot, new Set(['starter']));
|
|
197
|
+
(0, config_tree_1.ensurePortableSystemdFiles)(configRoot);
|
|
173
198
|
const agentDir = path.join(configRoot, profile);
|
|
199
|
+
migrateProfileOnStart(configRoot, agentDir, false);
|
|
174
200
|
(0, config_tree_1.copySkeletonTree)(path.join(sourcePath, 'starter', 'basic-project'), agentDir);
|
|
175
201
|
if (profile !== detectedProfile) {
|
|
176
202
|
writeProjectProfileMapping(projectRoot, profile);
|
|
@@ -193,9 +219,9 @@ function confirmProfile(detectedProfile) {
|
|
|
193
219
|
}
|
|
194
220
|
return requireValidProfile(promptLine('Profile (org/repo): '));
|
|
195
221
|
}
|
|
196
|
-
function promptLine(prompt) {
|
|
222
|
+
function promptLine(prompt, nonInteractiveMessage = 'setup requires a profile argument when input is not interactive') {
|
|
197
223
|
if (!process.stdin.isTTY) {
|
|
198
|
-
(0, utils_1.fail)(
|
|
224
|
+
(0, utils_1.fail)(nonInteractiveMessage);
|
|
199
225
|
}
|
|
200
226
|
process.stdout.write(prompt);
|
|
201
227
|
const bytes = [];
|
|
@@ -234,6 +260,7 @@ function runEdit(parsed) {
|
|
|
234
260
|
return;
|
|
235
261
|
}
|
|
236
262
|
const agentDir = initializeProfileSource(projectRoot, false);
|
|
263
|
+
migrateProfileOnStart((0, project_1.defaultConfigRoot)(projectRoot), agentDir, false);
|
|
237
264
|
const editPath = (0, manifest_1.createDefaultLocalFile)(agentDir);
|
|
238
265
|
(0, renderer_1.syncAgentProfile)(projectRoot, agentDir);
|
|
239
266
|
process.stdout.write(`Edit: ${editPath}\n`);
|
|
@@ -254,6 +281,7 @@ function runUpdate(parsed) {
|
|
|
254
281
|
const agentDir = (0, project_1.resolveAgentDir)(projectRoot);
|
|
255
282
|
prepareConfigRoot(configRoot);
|
|
256
283
|
requireProfile(null, agentDir);
|
|
284
|
+
migrateProfileOnStart(configRoot, agentDir, false);
|
|
257
285
|
printUpdateSummary((0, renderer_1.syncAgentProfile)(projectRoot, agentDir));
|
|
258
286
|
}
|
|
259
287
|
function runUpdateAll(targetPath) {
|
|
@@ -264,6 +292,7 @@ function runUpdateAll(targetPath) {
|
|
|
264
292
|
prepareConfigRoot(configRoot);
|
|
265
293
|
const profileDirs = (0, config_tree_1.findProfileDirs)(configRoot);
|
|
266
294
|
for (const agentDir of profileDirs) {
|
|
295
|
+
migrateProfileOnStart(configRoot, agentDir, false);
|
|
267
296
|
const profile = path.relative(configRoot, agentDir).replace(/\\/g, '/');
|
|
268
297
|
const projectRoot = (0, project_1.projectRootForProfile)(profile, configRoot);
|
|
269
298
|
(0, renderer_1.syncAgentProfile)(projectRoot, agentDir, { configRoot, profile });
|
|
@@ -281,15 +310,16 @@ function runMigrateConfig(parsed) {
|
|
|
281
310
|
(0, config_tree_1.migrateOldTemplates)(configRoot);
|
|
282
311
|
(0, config_tree_1.ensureDefaultGlobalTemplates)(configRoot);
|
|
283
312
|
(0, manifest_1.ensureRootDefaultsFile)(configRoot);
|
|
284
|
-
const profileDirs = (0, config_tree_1.findLegacyProfileDirs)(configRoot);
|
|
313
|
+
const profileDirs = (0, utils_1.uniqueSorted)([...(0, config_tree_1.findLegacyProfileDirs)(configRoot), ...(0, config_tree_1.findProfileDirs)(configRoot)]);
|
|
285
314
|
let createdManifestCount = 0;
|
|
286
315
|
let createdLocalCount = 0;
|
|
287
316
|
let movedRuntimeCount = 0;
|
|
288
317
|
let movedReviewCount = 0;
|
|
289
318
|
let movedMemoryCount = 0;
|
|
290
319
|
for (const agentDir of profileDirs) {
|
|
320
|
+
const layout = migrateProfileOnStart(configRoot, agentDir, parsed.yes);
|
|
291
321
|
const localPath = path.join(agentDir, constants_1.LOCAL_TEMPLATE_FILE_NAME);
|
|
292
|
-
if (!fs.existsSync(localPath)) {
|
|
322
|
+
if (!fs.existsSync(localPath) && fs.existsSync(path.join(agentDir, 'AGENTS-MODS.md'))) {
|
|
293
323
|
fs.writeFileSync(localPath, (0, config_tree_1.convertLegacyTemplateVars)(fs.readFileSync(path.join(agentDir, 'AGENTS-MODS.md'), 'utf8')), 'utf8');
|
|
294
324
|
createdLocalCount += 1;
|
|
295
325
|
}
|
|
@@ -299,7 +329,7 @@ function runMigrateConfig(parsed) {
|
|
|
299
329
|
}
|
|
300
330
|
movedRuntimeCount += (0, config_tree_1.migrateCodexRuntimeFiles)(agentDir);
|
|
301
331
|
movedReviewCount += (0, config_tree_1.migrateLooseFiles)(agentDir, /^REVIEW(?:-.+)?\.md$/, 'reviews');
|
|
302
|
-
movedMemoryCount +=
|
|
332
|
+
movedMemoryCount += layout.projectMemoryMoves;
|
|
303
333
|
}
|
|
304
334
|
process.stdout.write(`OK migrated ${configRoot}\n`);
|
|
305
335
|
process.stdout.write(`Profiles converted: ${profileDirs.length}\n`);
|
|
@@ -309,6 +339,29 @@ function runMigrateConfig(parsed) {
|
|
|
309
339
|
process.stdout.write(`Moved review files: ${movedReviewCount}\n`);
|
|
310
340
|
process.stdout.write(`Moved memory files: ${movedMemoryCount}\n`);
|
|
311
341
|
}
|
|
342
|
+
function migrateProfileOnStart(configRoot, profileDir, assumeYes) {
|
|
343
|
+
const result = (0, config_tree_1.migrateProfileLayout)(configRoot, profileDir, ({ gitRoot, moves }) => {
|
|
344
|
+
process.stdout.write('agent-run: this profile uses an older project-memory layout.\n');
|
|
345
|
+
process.stdout.write(`Git working tree: ${gitRoot}\n`);
|
|
346
|
+
for (const move of moves) {
|
|
347
|
+
process.stdout.write(` ${path.relative(gitRoot, move.source)} -> ${path.relative(gitRoot, move.target)}\n`);
|
|
348
|
+
}
|
|
349
|
+
if (assumeYes) {
|
|
350
|
+
return true;
|
|
351
|
+
}
|
|
352
|
+
const migrateCommand = (0, utils_1.formatCommand)('agent-run', ['migrate-config', '--yes', configRoot]);
|
|
353
|
+
const message = `tracked project-memory files need migration; rerun interactively or run \`${migrateCommand}\``;
|
|
354
|
+
const answer = promptLine('Stage these moves with git mv? [Y/n] ', message).trim().toLowerCase();
|
|
355
|
+
return answer === '' || answer === 'y' || answer === 'yes';
|
|
356
|
+
});
|
|
357
|
+
if (result.gitMoves > 0) {
|
|
358
|
+
process.stdout.write(`Staged Git moves: ${result.gitMoves}\n`);
|
|
359
|
+
}
|
|
360
|
+
if (result.runtimeMoves > 0) {
|
|
361
|
+
process.stdout.write(`Moved local Codex runtime entries: ${result.runtimeMoves}\n`);
|
|
362
|
+
}
|
|
363
|
+
return result;
|
|
364
|
+
}
|
|
312
365
|
function runCheck(parsed) {
|
|
313
366
|
if (parsed.all) {
|
|
314
367
|
const report = (0, checks_1.checkSourceTree)(parsed.targetPath);
|
|
@@ -328,6 +381,7 @@ function prepareConfigRoot(configRoot) {
|
|
|
328
381
|
(0, config_tree_1.ensureConfigRootLayout)(configRoot);
|
|
329
382
|
(0, config_tree_1.ensureConfigRootGitignore)(configRoot);
|
|
330
383
|
(0, config_tree_1.ensureDefaultGlobalTemplates)(configRoot);
|
|
384
|
+
(0, config_tree_1.ensurePortableSystemdFiles)(configRoot);
|
|
331
385
|
}
|
|
332
386
|
function activeProject(targetPath, action) {
|
|
333
387
|
const projectRoot = (0, project_1.findProjectRoot)(targetPath);
|
|
@@ -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
|
+
}
|