@technomoron/agent-run 0.99.24 → 0.99.25
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 +10 -0
- package/README.md +56 -30
- package/dist/agent-run.js +13 -3213
- package/dist/checks.js +110 -0
- package/dist/cli.js +369 -0
- package/dist/commands.js +291 -0
- package/dist/config-tree.js +170 -0
- package/dist/constants.js +62 -0
- package/dist/defaults.js +408 -0
- package/dist/guards.js +163 -0
- package/dist/manifest.js +190 -0
- package/dist/model.js +2 -0
- package/dist/process.js +208 -0
- package/dist/project.js +316 -0
- package/dist/renderer.js +441 -0
- package/dist/templates.js +203 -0
- package/dist/tools.js +93 -0
- package/dist/utils.js +117 -0
- package/dist/walk-tree.js +24 -0
- package/examples/basic-config/README.md +3 -3
- package/examples/basic-config/agent-config/agent-run.defaults.jsonc +38 -0
- package/examples/basic-config/agent-config/global/tool-templates/AGENTS.md.njk +1 -1
- package/examples/basic-config/agent-config/starter/basic-project/agent-run.jsonc +1 -21
- package/package.json +1 -1
package/dist/tools.js
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.runCodex = runCodex;
|
|
4
|
+
exports.runClaude = runClaude;
|
|
5
|
+
exports.getPermissionArgs = getPermissionArgs;
|
|
6
|
+
const fs = require("fs");
|
|
7
|
+
const path = require("path");
|
|
8
|
+
const process_1 = require("./process");
|
|
9
|
+
const project_1 = require("./project");
|
|
10
|
+
function runCodex(realBinary, permissionArgs, context, args, wrapperArgs) {
|
|
11
|
+
const liveDir = context.paths.liveDir;
|
|
12
|
+
const codexHomeDir = context.paths.codexHomeDir;
|
|
13
|
+
const projectRoot = context.projectRoot;
|
|
14
|
+
if (!fs.existsSync(path.join(codexHomeDir, 'AGENTS.md'))) {
|
|
15
|
+
failMissingConfig('codex', context.profileDir);
|
|
16
|
+
}
|
|
17
|
+
const realPath = process.env.AGENT_RUN_REAL_PATH ?? process.env.PATH ?? '';
|
|
18
|
+
fs.mkdirSync(codexHomeDir, { recursive: true });
|
|
19
|
+
const env = {
|
|
20
|
+
...process.env,
|
|
21
|
+
CODEX_HOME: codexHomeDir,
|
|
22
|
+
AGENT_DIR: liveDir,
|
|
23
|
+
AGENT_PROFILE_DIR: context.profileDir,
|
|
24
|
+
AGENT_RUN_PROJECT_ROOT: projectRoot,
|
|
25
|
+
AGENT_RUN_REAL_PATH: realPath,
|
|
26
|
+
PATH: `${path.join(liveDir, 'bin')}${path.delimiter}${realPath}`
|
|
27
|
+
};
|
|
28
|
+
const runtimeArgs = wrapperArgs.codexSandboxMode === 'danger'
|
|
29
|
+
? ['-a', 'never', '-s', 'danger-full-access']
|
|
30
|
+
: ['-a', 'on-request', '-s', 'workspace-write'];
|
|
31
|
+
(0, process_1.execCommand)(realBinary, [
|
|
32
|
+
...permissionArgs,
|
|
33
|
+
...runtimeArgs,
|
|
34
|
+
'-C',
|
|
35
|
+
projectRoot,
|
|
36
|
+
...(wrapperArgs.codexSandboxMode !== 'danger' && wrapperArgs.codexNetwork
|
|
37
|
+
? ['--config', 'sandbox_workspace_write.network_access=true']
|
|
38
|
+
: []),
|
|
39
|
+
...args
|
|
40
|
+
], env, codexHomeDir, (code) => postflightProjectCheck(context, wrapperArgs.local, code));
|
|
41
|
+
}
|
|
42
|
+
function runClaude(realBinary, permissionArgs, context, args, wrapperArgs) {
|
|
43
|
+
const liveDir = context.paths.liveDir;
|
|
44
|
+
const claudePath = path.join(liveDir, 'CLAUDE.md');
|
|
45
|
+
if (!fs.existsSync(claudePath)) {
|
|
46
|
+
failMissingConfig('claude', context.profileDir);
|
|
47
|
+
}
|
|
48
|
+
const claudeConfigDir = path.join(liveDir, '.claude');
|
|
49
|
+
const realPath = process.env.AGENT_RUN_REAL_PATH ?? process.env.PATH ?? '';
|
|
50
|
+
const env = {
|
|
51
|
+
...process.env,
|
|
52
|
+
AGENT_DIR: liveDir,
|
|
53
|
+
AGENT_PROFILE_DIR: context.profileDir,
|
|
54
|
+
AGENT_RUN_PROJECT_ROOT: context.projectRoot,
|
|
55
|
+
AGENT_RUN_REAL_PATH: realPath,
|
|
56
|
+
PATH: `${path.join(liveDir, 'bin')}${path.delimiter}${realPath}`
|
|
57
|
+
};
|
|
58
|
+
(0, process_1.execCommand)(realBinary, [
|
|
59
|
+
...permissionArgs,
|
|
60
|
+
'--append-system-prompt-file',
|
|
61
|
+
claudePath,
|
|
62
|
+
'--settings',
|
|
63
|
+
path.join(claudeConfigDir, 'agent-run-settings.json'),
|
|
64
|
+
'--plugin-dir',
|
|
65
|
+
claudeConfigDir,
|
|
66
|
+
...args
|
|
67
|
+
], env, context.projectRoot, (code) => postflightProjectCheck(context, wrapperArgs.local, code));
|
|
68
|
+
}
|
|
69
|
+
function postflightProjectCheck(context, allowLocal, code) {
|
|
70
|
+
if (!context.guardrails.forbidRepoAiFiles) {
|
|
71
|
+
return code;
|
|
72
|
+
}
|
|
73
|
+
const localAiFiles = (0, project_1.findLocalAiFiles)(context.projectRoot, context.profileDir);
|
|
74
|
+
if (localAiFiles.length === 0) {
|
|
75
|
+
return code;
|
|
76
|
+
}
|
|
77
|
+
(0, project_1.warnForLocalAiFiles)(null, context.projectRoot, localAiFiles);
|
|
78
|
+
return allowLocal ? code : 1;
|
|
79
|
+
}
|
|
80
|
+
function failMissingConfig(tool, agentDir) {
|
|
81
|
+
process.stderr.write(`agent-run: no ${tool} config found for this project: ${agentDir}\n`);
|
|
82
|
+
process.stderr.write(`agent-run: run \`agent-run ${tool} --none\` to bypass agent setup, or \`agent-run ${tool} --create\` to create profile files.\n`);
|
|
83
|
+
process.exit(1);
|
|
84
|
+
}
|
|
85
|
+
function getPermissionArgs(tool) {
|
|
86
|
+
if (process.env.AGENT_WRAPPER_FORCE_PERMISSIVE !== '1') {
|
|
87
|
+
return [];
|
|
88
|
+
}
|
|
89
|
+
if (tool === 'claude' && typeof process.getuid === 'function' && process.getuid() !== 0) {
|
|
90
|
+
return ['--permission-mode', 'bypassPermissions'];
|
|
91
|
+
}
|
|
92
|
+
return [];
|
|
93
|
+
}
|
package/dist/utils.js
ADDED
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.isSamePathOrDescendant = isSamePathOrDescendant;
|
|
4
|
+
exports.parseBooleanEnv = parseBooleanEnv;
|
|
5
|
+
exports.localDateString = localDateString;
|
|
6
|
+
exports.formatError = formatError;
|
|
7
|
+
exports.verbose = verbose;
|
|
8
|
+
exports.formatCommand = formatCommand;
|
|
9
|
+
exports.fail = fail;
|
|
10
|
+
exports.uniqueSorted = uniqueSorted;
|
|
11
|
+
exports.formatPathList = formatPathList;
|
|
12
|
+
exports.getExecutableExtensions = getExecutableExtensions;
|
|
13
|
+
exports.isExecutable = isExecutable;
|
|
14
|
+
exports.findExecutable = findExecutable;
|
|
15
|
+
exports.isSymlink = isSymlink;
|
|
16
|
+
exports.nextBackupPath = nextBackupPath;
|
|
17
|
+
const fs = require("fs");
|
|
18
|
+
const path = require("path");
|
|
19
|
+
const constants_1 = require("./constants");
|
|
20
|
+
function isSamePathOrDescendant(candidatePath, parentPath) {
|
|
21
|
+
const relative = path.relative(parentPath, candidatePath);
|
|
22
|
+
return relative === '' || (!relative.startsWith(`..${path.sep}`) && relative !== '..' && !path.isAbsolute(relative));
|
|
23
|
+
}
|
|
24
|
+
function parseBooleanEnv(value) {
|
|
25
|
+
if (value === undefined) {
|
|
26
|
+
return false;
|
|
27
|
+
}
|
|
28
|
+
return ['1', 'true', 'yes', 'on'].includes(value.trim().toLowerCase());
|
|
29
|
+
}
|
|
30
|
+
function localDateString() {
|
|
31
|
+
const now = new Date();
|
|
32
|
+
const year = String(now.getFullYear()).padStart(4, '0');
|
|
33
|
+
const month = String(now.getMonth() + 1).padStart(2, '0');
|
|
34
|
+
const day = String(now.getDate()).padStart(2, '0');
|
|
35
|
+
return `${year}-${month}-${day}`;
|
|
36
|
+
}
|
|
37
|
+
function formatError(error) {
|
|
38
|
+
return error instanceof Error ? error.message : String(error);
|
|
39
|
+
}
|
|
40
|
+
function verbose(message) {
|
|
41
|
+
if (parseBooleanEnv(process.env[constants_1.VERBOSE_ENV])) {
|
|
42
|
+
process.stderr.write(`agent-run: ${message}\n`);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
function formatCommand(command, args) {
|
|
46
|
+
return [command, ...args].map(quoteArg).join(' ');
|
|
47
|
+
}
|
|
48
|
+
function quoteArg(value) {
|
|
49
|
+
if (value === '') {
|
|
50
|
+
return '""';
|
|
51
|
+
}
|
|
52
|
+
if (/^[A-Za-z0-9_./:=+-]+$/.test(value)) {
|
|
53
|
+
return value;
|
|
54
|
+
}
|
|
55
|
+
return JSON.stringify(value);
|
|
56
|
+
}
|
|
57
|
+
function fail(message) {
|
|
58
|
+
process.stderr.write(`agent-run: ${message}\n`);
|
|
59
|
+
process.exit(1);
|
|
60
|
+
}
|
|
61
|
+
function uniqueSorted(values) {
|
|
62
|
+
return [...new Set(values.map((value) => path.resolve(value)))].sort((a, b) => a.localeCompare(b));
|
|
63
|
+
}
|
|
64
|
+
function formatPathList(paths) {
|
|
65
|
+
return paths.length === 0 ? [' (none)'] : paths.map((entry) => ` ${entry}`);
|
|
66
|
+
}
|
|
67
|
+
function getExecutableExtensions(tool) {
|
|
68
|
+
if (!constants_1.IS_WINDOWS || path.extname(tool)) {
|
|
69
|
+
return [''];
|
|
70
|
+
}
|
|
71
|
+
return (process.env.PATHEXT || '.EXE;.CMD;.BAT;.COM')
|
|
72
|
+
.split(';')
|
|
73
|
+
.filter(Boolean)
|
|
74
|
+
.map((entry) => entry.toLowerCase());
|
|
75
|
+
}
|
|
76
|
+
function isExecutable(filePath) {
|
|
77
|
+
try {
|
|
78
|
+
if (constants_1.IS_WINDOWS) {
|
|
79
|
+
return fs.statSync(filePath).isFile();
|
|
80
|
+
}
|
|
81
|
+
fs.accessSync(filePath, fs.constants.X_OK);
|
|
82
|
+
return true;
|
|
83
|
+
}
|
|
84
|
+
catch {
|
|
85
|
+
return false;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
function findExecutable(command) {
|
|
89
|
+
const pathDirs = (process.env.PATH || '').split(path.delimiter).filter(Boolean);
|
|
90
|
+
for (const dir of pathDirs) {
|
|
91
|
+
for (const extension of getExecutableExtensions(command)) {
|
|
92
|
+
const candidate = path.join(dir, `${command}${extension}`);
|
|
93
|
+
if (fs.existsSync(candidate) && isExecutable(candidate)) {
|
|
94
|
+
return candidate;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
return null;
|
|
99
|
+
}
|
|
100
|
+
function isSymlink(filePath) {
|
|
101
|
+
try {
|
|
102
|
+
return fs.lstatSync(filePath).isSymbolicLink();
|
|
103
|
+
}
|
|
104
|
+
catch {
|
|
105
|
+
return false;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
function nextBackupPath(filePath) {
|
|
109
|
+
const timestamp = new Date().toISOString().replace(/[-:TZ.]/g, '').slice(0, 14);
|
|
110
|
+
let candidate = `${filePath}.bak.${timestamp}`;
|
|
111
|
+
let index = 1;
|
|
112
|
+
while (fs.existsSync(candidate) || isSymlink(candidate)) {
|
|
113
|
+
candidate = `${filePath}.bak.${timestamp}.${index}`;
|
|
114
|
+
index += 1;
|
|
115
|
+
}
|
|
116
|
+
return candidate;
|
|
117
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.walkTree = walkTree;
|
|
4
|
+
const fs = require("fs");
|
|
5
|
+
const path = require("path");
|
|
6
|
+
function walkTree(dir, visitor, options = {}) {
|
|
7
|
+
if (options.shouldSkipDirectory?.(dir)) {
|
|
8
|
+
return;
|
|
9
|
+
}
|
|
10
|
+
const entries = fs.readdirSync(dir, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name));
|
|
11
|
+
for (const entry of entries) {
|
|
12
|
+
if (options.shouldPrune?.(entry)) {
|
|
13
|
+
continue;
|
|
14
|
+
}
|
|
15
|
+
const fullPath = path.join(dir, entry.name);
|
|
16
|
+
if (entry.isDirectory() && options.shouldSkipDirectory?.(fullPath)) {
|
|
17
|
+
continue;
|
|
18
|
+
}
|
|
19
|
+
const result = visitor(fullPath, entry);
|
|
20
|
+
if (entry.isDirectory() && result !== 'skip') {
|
|
21
|
+
walkTree(fullPath, visitor, options);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -7,9 +7,9 @@ It contains two pieces:
|
|
|
7
7
|
|
|
8
8
|
- `project/`: a minimal project with `.agent-run.env` pointing at the bundled
|
|
9
9
|
config root.
|
|
10
|
-
- `agent-config/`: a complete config root with
|
|
11
|
-
skill templates, a personal memory skill, a
|
|
12
|
-
instructions, and profile overrides.
|
|
10
|
+
- `agent-config/`: a complete config root with shared manifest defaults, global
|
|
11
|
+
templates, snippets, skill templates, a personal memory skill, a sparse
|
|
12
|
+
profile manifest, profile-local instructions, and profile overrides.
|
|
13
13
|
|
|
14
14
|
Try it from this repository after building:
|
|
15
15
|
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
{
|
|
2
|
+
// Shared defaults; profile manifests contain only differences.
|
|
3
|
+
"kind": "code",
|
|
4
|
+
"agent": {
|
|
5
|
+
"base": "global/agents/code.md.njk",
|
|
6
|
+
"includes": [
|
|
7
|
+
"{{ profile }}/local.md.njk"
|
|
8
|
+
]
|
|
9
|
+
},
|
|
10
|
+
"skills": {
|
|
11
|
+
"install": [
|
|
12
|
+
"commit-workflow",
|
|
13
|
+
"github-release",
|
|
14
|
+
"release-package-check",
|
|
15
|
+
"code-review-organizer"
|
|
16
|
+
]
|
|
17
|
+
},
|
|
18
|
+
"tools": {
|
|
19
|
+
"codex": true,
|
|
20
|
+
"claude": true
|
|
21
|
+
},
|
|
22
|
+
"checks": [
|
|
23
|
+
"agent-run check .",
|
|
24
|
+
"pnpm run cleanbuild"
|
|
25
|
+
],
|
|
26
|
+
"guardrails": {
|
|
27
|
+
"blockGitWrite": true,
|
|
28
|
+
"blockPublish": true,
|
|
29
|
+
"blockGithubRelease": true,
|
|
30
|
+
"forbidRepoAiFiles": true
|
|
31
|
+
},
|
|
32
|
+
"paths": {
|
|
33
|
+
"changesFile": "{{ projectRoot }}/CHANGES",
|
|
34
|
+
"reviewDir": "{{ profileDir }}/reviews",
|
|
35
|
+
"reviewFile": "{{ profileDir }}/reviews/REVIEW-{{ date }}.md",
|
|
36
|
+
"memoriesDir": "{{ agentDir }}/memories"
|
|
37
|
+
}
|
|
38
|
+
}
|
|
@@ -1,8 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"profile": "starter/basic-project",
|
|
3
|
-
"kind": "code",
|
|
4
2
|
"agent": {
|
|
5
|
-
"base": "global/agents/code.md.njk",
|
|
6
3
|
"includes": [
|
|
7
4
|
"{{ profile }}/local.md.njk",
|
|
8
5
|
"{{ profile }}/project-rules.md.njk"
|
|
@@ -20,25 +17,8 @@
|
|
|
20
17
|
"triage": "{{ profile }}/overrides/triage.SKILL.md.njk"
|
|
21
18
|
}
|
|
22
19
|
},
|
|
23
|
-
"tools": {
|
|
24
|
-
"codex": true,
|
|
25
|
-
"claude": true
|
|
26
|
-
},
|
|
27
20
|
"checks": [
|
|
28
21
|
"agent-run check .",
|
|
29
|
-
"pnpm run cleanbuild",
|
|
30
22
|
"pnpm test"
|
|
31
|
-
]
|
|
32
|
-
"guardrails": {
|
|
33
|
-
"blockGitWrite": true,
|
|
34
|
-
"blockPublish": true,
|
|
35
|
-
"blockGithubRelease": true,
|
|
36
|
-
"forbidRepoAiFiles": true
|
|
37
|
-
},
|
|
38
|
-
"paths": {
|
|
39
|
-
"changesFile": "{{ projectRoot }}/CHANGES",
|
|
40
|
-
"reviewDir": "{{ profileDir }}/reviews",
|
|
41
|
-
"reviewFile": "{{ profileDir }}/reviews/REVIEW-{{ date }}.md",
|
|
42
|
-
"memoriesDir": "{{ agentDir }}/memories"
|
|
43
|
-
}
|
|
23
|
+
]
|
|
44
24
|
}
|